guix-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug#30111] gnu: address@hidden: Apply the 'retpoline' mitigation techni


From: Alex Vong
Subject: [bug#30111] gnu: address@hidden: Apply the 'retpoline' mitigation technique.
Date: Tue, 16 Jan 2018 21:14:54 +0800
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/25.3 (gnu/linux)

Hello,

This is the new patch. Similar to last time, I haven't test it yet, but
I will report if it doesn't build.

>From aea3d11f59e260111bdb8bcac458c97a946fa900 Mon Sep 17 00:00:00 2001
From: Alex Vong <address@hidden>
Date: Tue, 16 Jan 2018 20:32:32 +0800
Subject: [PATCH] gnu: address@hidden: Apply the 'retpoline' mitigation 
technique.

This is part of Spectre (branch target injection) [CVE-2017-5715]
mitigation. Suggested by Mark H Weaver <address@hidden>.

* gnu/local.mk (dist_patch_DATA): Add them.
* gnu/packages/gcc.scm (address@hidden): Use them.
* gnu/packages/patches/gcc-retpoline-Change-V-to-bare-reg-names.patch,
gnu/packages/patches/gcc-retpoline-i386-More-use-reference-of-struct-ix86_frame-to-avoi.patch,
gnu/packages/patches/gcc-retpoline-i386-Move-struct-ix86_frame-to-machine_function.patch,
gnu/packages/patches/gcc-retpoline-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.patch,
gnu/packages/patches/gcc-retpoline-indirect-thunk-reg-names.patch,
gnu/packages/patches/gcc-retpoline-x86-Add-V-register-operand-modifier.patch,
gnu/packages/patches/gcc-retpoline-x86-Add-mfunction-return.patch,
gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch-register.patch,
gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch.patch,
gnu/packages/patches/gcc-retpoline-x86-Disallow-mindirect-branch-mfunction-return-with-.patch:
New files.
---
 gnu/local.mk                                       |   12 +-
 gnu/packages/gcc.scm                               |   13 +-
 .../gcc-retpoline-Change-V-to-bare-reg-names.patch |   51 +
 ...se-reference-of-struct-ix86_frame-to-avoi.patch |   69 +
 ...ove-struct-ix86_frame-to-machine_function.patch |  252 +++
 ...ference-of-struct-ix86_frame-to-avoid-cop.patch |   83 +
 .../gcc-retpoline-indirect-thunk-reg-names.patch   |  365 ++++
 ...oline-x86-Add-V-register-operand-modifier.patch |  143 ++
 .../gcc-retpoline-x86-Add-mfunction-return.patch   | 1303 ++++++++++++
 ...tpoline-x86-Add-mindirect-branch-register.patch |  907 ++++++++
 .../gcc-retpoline-x86-Add-mindirect-branch.patch   | 2171 ++++++++++++++++++++
 ...w-mindirect-branch-mfunction-return-with-.patch |  308 +++
 12 files changed, 5675 insertions(+), 2 deletions(-)
 create mode 100644 
gnu/packages/patches/gcc-retpoline-Change-V-to-bare-reg-names.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-i386-More-use-reference-of-struct-ix86_frame-to-avoi.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-i386-Move-struct-ix86_frame-to-machine_function.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-indirect-thunk-reg-names.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-x86-Add-V-register-operand-modifier.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-x86-Add-mfunction-return.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch-register.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch.patch
 create mode 100644 
gnu/packages/patches/gcc-retpoline-x86-Disallow-mindirect-branch-mfunction-return-with-.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index fb4babfdb..b84d0b545 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -9,7 +9,7 @@
 # Copyright © 2016 Adonay "adfeno" Felipe Nogueira 
<https://libreplanet.org/wiki/User:Adfeno> <address@hidden>
 # Copyright © 2016, 2017 Ricardo Wurmus <address@hidden>
 # Copyright © 2016 Ben Woodcroft <address@hidden>
-# Copyright © 2016, 2017 Alex Vong <address@hidden>
+# Copyright © 2016, 2017, 2018 Alex Vong <address@hidden>
 # Copyright © 2016, 2017 Efraim Flashner <address@hidden>
 # Copyright © 2016, 2017 Jan Nieuwenhuizen <address@hidden>
 # Copyright © 2017 Tobias Geerinckx-Rice <address@hidden>
@@ -654,6 +654,16 @@ dist_patch_DATA =                                          
\
   %D%/packages/patches/gcc-asan-powerpc-missing-include.patch  \
   %D%/packages/patches/gcc-cross-environment-variables.patch   \
   %D%/packages/patches/gcc-libvtv-runpath.patch                        \
+  
%D%/packages/patches/gcc-retpoline-i386-Move-struct-ix86_frame-to-machine_function.patch
     \
+  
%D%/packages/patches/gcc-retpoline-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.patch
        \
+  
%D%/packages/patches/gcc-retpoline-i386-More-use-reference-of-struct-ix86_frame-to-avoi.patch
        \
+  %D%/packages/patches/gcc-retpoline-x86-Add-mindirect-branch.patch    \
+  %D%/packages/patches/gcc-retpoline-x86-Add-mfunction-return.patch    \
+  %D%/packages/patches/gcc-retpoline-x86-Add-mindirect-branch-register.patch   
\
+  %D%/packages/patches/gcc-retpoline-x86-Add-V-register-operand-modifier.patch 
\
+  
%D%/packages/patches/gcc-retpoline-x86-Disallow-mindirect-branch-mfunction-return-with-.patch
        \
+  %D%/packages/patches/gcc-retpoline-Change-V-to-bare-reg-names.patch  \
+  %D%/packages/patches/gcc-retpoline-indirect-thunk-reg-names.patch    \
   %D%/packages/patches/gcc-strmov-store-file-names.patch       \
   %D%/packages/patches/gcc-4-compile-with-gcc-5.patch           \
   %D%/packages/patches/gcc-4.6-gnu-inline.patch                        \
diff --git a/gnu/packages/gcc.scm b/gnu/packages/gcc.scm
index ad8992289..89d2ab7fd 100644
--- a/gnu/packages/gcc.scm
+++ b/gnu/packages/gcc.scm
@@ -5,6 +5,7 @@
 ;;; Copyright © 2015 Andreas Enge <address@hidden>
 ;;; Copyright © 2015, 2016, 2017 Efraim Flashner <address@hidden>
 ;;; Copyright © 2016 Carlos Sánchez de La Lama <address@hidden>
+;;; Copyright © 2018 ALex Vong <address@hidden>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -427,7 +428,17 @@ Go.  It also includes runtime support libraries for these 
languages.")
                (base32
                 "16j7i0888j2f1yp9l0nhji6cq65dy6y4nwy8868a8njbzzwavxqw"))
               (patches (search-patches "gcc-strmov-store-file-names.patch"
-                                       "gcc-5.0-libvtv-runpath.patch"))))
+                                       "gcc-5.0-libvtv-runpath.patch"
+                                       
"gcc-retpoline-i386-Move-struct-ix86_frame-to-machine_function.patch"
+                                       
"gcc-retpoline-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.patch"
+                                       
"gcc-retpoline-i386-More-use-reference-of-struct-ix86_frame-to-avoi.patch"
+                                       
"gcc-retpoline-x86-Add-mindirect-branch.patch"
+                                       
"gcc-retpoline-x86-Add-mfunction-return.patch"
+                                       
"gcc-retpoline-x86-Add-mindirect-branch-register.patch"
+                                       
"gcc-retpoline-x86-Add-V-register-operand-modifier.patch"
+                                       
"gcc-retpoline-x86-Disallow-mindirect-branch-mfunction-return-with-.patch"
+                                       
"gcc-retpoline-Change-V-to-bare-reg-names.patch"
+                                       
"gcc-retpoline-indirect-thunk-reg-names.patch"))))
     (description
      "GCC is the GNU Compiler Collection.  It provides compiler front-ends
 for several languages, including C, C++, Objective-C, Fortran, Ada, and Go.
diff --git 
a/gnu/packages/patches/gcc-retpoline-Change-V-to-bare-reg-names.patch 
b/gnu/packages/patches/gcc-retpoline-Change-V-to-bare-reg-names.patch
new file mode 100644
index 000000000..1d893a621
--- /dev/null
+++ b/gnu/packages/patches/gcc-retpoline-Change-V-to-bare-reg-names.patch
@@ -0,0 +1,51 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From c26d2b96599ebc9ff24c685a2dc3b01709aa3cce Mon Sep 17 00:00:00 2001
+From: David Woodhouse <address@hidden>
+Date: Sun, 14 Jan 2018 21:27:35 +0000
+Subject: [PATCH 09/10] Change %V to bare reg names
+
+---
+ gcc/config/i386/i386.c                                    | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index 318a71840c9..6d345031a82 100644
+--- a/gcc/config/i386/i386.c
++++ b/gcc/config/i386/i386.c
+@@ -18029,7 +18029,7 @@ print_reg (rtx x, int code, FILE *file)
+       warning (0, "unsupported size for integer register");
+       /* FALLTHRU */
+     case 4:
+-      if (LEGACY_INT_REGNO_P (regno))
++      if (LEGACY_INT_REGNO_P (regno) && code != 'V')
+       putc (msize > 4 && TARGET_64BIT ? 'r' : 'e', file);
+       /* FALLTHRU */
+     case 2:
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
+index f0cd9b75be8..6791890944c 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
+@@ -9,5 +9,5 @@ foo (void)
+   asm("call __x86_indirect_thunk_%V0" : : "a" (func_p));
+ }
+ 
+-/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_eax" { 
target ia32 } } } */
+-/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_rax" { 
target { ! ia32 } } } } */
++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_ax" { target 
ia32 } } } */
++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_ax" { target 
{ ! ia32 } } } } */
+-- 
+2.15.1
+
diff --git 
a/gnu/packages/patches/gcc-retpoline-i386-More-use-reference-of-struct-ix86_frame-to-avoi.patch
 
b/gnu/packages/patches/gcc-retpoline-i386-More-use-reference-of-struct-ix86_frame-to-avoi.patch
new file mode 100644
index 000000000..e115e3f6c
--- /dev/null
+++ 
b/gnu/packages/patches/gcc-retpoline-i386-More-use-reference-of-struct-ix86_frame-to-avoi.patch
@@ -0,0 +1,69 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From ec4a7ca4051bb5cbefe03a2e1fb690b9738b8c6d Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <address@hidden>
+Date: Tue, 28 Nov 2017 10:26:35 -0800
+Subject: [PATCH 03/10] i386: More use reference of struct ix86_frame to avoid
+ copy
+
+When there is no need to make a copy of ix86_frame, we can use reference
+of struct ix86_frame to avoid copy.
+
+       Backport from mainline
+       * config/i386/i386.c (ix86_expand_prologue): Use reference of
+       struct ix86_frame.
+       (ix86_expand_epilogue): Likewise.
+---
+ gcc/config/i386/i386.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+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
+-- 
+2.15.1
+
diff --git 
a/gnu/packages/patches/gcc-retpoline-i386-Move-struct-ix86_frame-to-machine_function.patch
 
b/gnu/packages/patches/gcc-retpoline-i386-Move-struct-ix86_frame-to-machine_function.patch
new file mode 100644
index 000000000..b55ee3407
--- /dev/null
+++ 
b/gnu/packages/patches/gcc-retpoline-i386-Move-struct-ix86_frame-to-machine_function.patch
@@ -0,0 +1,252 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From f23f45109139911714e2164191c0228500ebef92 Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <address@hidden>
+Date: Mon, 6 Nov 2017 09:11:08 -0800
+Subject: [PATCH 01/10] i386: Move struct ix86_frame to machine_function
+
+Make ix86_frame available to i386 code generation.  This is needed to
+backport the patch set of -mindirect-branch= to mitigate variant #2 of
+the speculative execution vulnerabilities on x86 processors identified
+by CVE-2017-5715, aka Spectre.
+
+       Backport from mainline
+       * config/i386/i386.c (ix86_frame): Moved to ...
+       * config/i386/i386.h (ix86_frame): Here.
+       (machine_function): Add frame.
+       * config/i386/i386.c (ix86_compute_frame_layout): Repace the
+       frame argument with &cfun->machine->frame.
+       (ix86_can_use_return_insn_p): Don't pass &frame to
+       ix86_compute_frame_layout.  Copy frame from cfun->machine->frame.
+       (ix86_can_eliminate): Likewise.
+       (ix86_expand_prologue): Likewise.
+       (ix86_expand_epilogue): Likewise.
+       (ix86_expand_split_stack_prologue): Likewise.
+---
+ gcc/config/i386/i386.c | 68 ++++++++++----------------------------------------
+ gcc/config/i386/i386.h | 53 ++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 65 insertions(+), 56 deletions(-)
+
+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
+-- 
+2.15.1
+
diff --git 
a/gnu/packages/patches/gcc-retpoline-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.patch
 
b/gnu/packages/patches/gcc-retpoline-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.patch
new file mode 100644
index 000000000..30eca1a43
--- /dev/null
+++ 
b/gnu/packages/patches/gcc-retpoline-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.patch
@@ -0,0 +1,83 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From ca658cd57c02a81327ec09474e24f0688ac1a190 Mon Sep 17 00:00:00 2001
+From: hjl <address@hidden>
+Date: Mon, 6 Nov 2017 23:04:15 +0000
+Subject: [PATCH 02/10] 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.
+
+       Backport from mainline
+       * config/i386/i386.c (ix86_can_use_return_insn_p): Use reference
+       of struct ix86_frame.
+       (ix86_initial_elimination_offset): Likewise.
+       (ix86_expand_split_stack_prologue): Likewise.
+---
+ gcc/config/i386/i386.c | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+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
+-- 
+2.15.1
+
diff --git a/gnu/packages/patches/gcc-retpoline-indirect-thunk-reg-names.patch 
b/gnu/packages/patches/gcc-retpoline-indirect-thunk-reg-names.patch
new file mode 100644
index 000000000..6da7b48c3
--- /dev/null
+++ b/gnu/packages/patches/gcc-retpoline-indirect-thunk-reg-names.patch
@@ -0,0 +1,365 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From f6d256da9cefa1409598fa5ffe632647a30ad6f9 Mon Sep 17 00:00:00 2001
+From: David Woodhouse <address@hidden>
+Date: Sun, 14 Jan 2018 21:36:59 +0000
+Subject: [PATCH 10/10] indirect thunk reg names
+
+---
+ gcc/config/i386/i386.c                                    | 9 ++-------
+ gcc/testsuite/gcc.target/i386/indirect-thunk-1.c          | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-2.c          | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-3.c          | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-4.c          | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-7.c          | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c     | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c     | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c     | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c     | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c     | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c   | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c   | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c   | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c   | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c   | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c | 2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c | 2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-10.c              | 4 ++--
+ gcc/testsuite/gcc.target/i386/ret-thunk-11.c              | 4 ++--
+ gcc/testsuite/gcc.target/i386/ret-thunk-12.c              | 4 ++--
+ gcc/testsuite/gcc.target/i386/ret-thunk-13.c              | 2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-14.c              | 2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-15.c              | 2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-9.c               | 2 +-
+ 25 files changed, 29 insertions(+), 34 deletions(-)
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index 6d345031a82..89eb68032a2 100644
+--- a/gcc/config/i386/i386.c
++++ b/gcc/config/i386/i386.c
+@@ -12052,13 +12052,8 @@ indirect_thunk_name (char name[32], int regno, bool 
need_bnd_p,
+       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]);
++        sprintf (name, "__x86_indirect_thunk%s_%s",
++                 bnd, reg_names[regno]);
+       }
+       else
+       {
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+index 9eb9b273ade..fb56b2db3b6 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+@@ -13,7 +13,7 @@ male_indirect_jump (long 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\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler {\tpause} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+index c63795e4127..337f455aa44 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+@@ -13,7 +13,7 @@ male_indirect_jump (long 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\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler {\tpause} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+index 82973cda771..2e40ec71609 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+@@ -14,7 +14,7 @@ male_indirect_jump (long 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 "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { 
target x32 } } } */
++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler {\tpause} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+index a5f3d1cbed8..309d1f6c10b 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+@@ -14,7 +14,7 @@ male_indirect_jump (long 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 "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { 
target x32 } } } */
++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler {\tpause} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+index ebfb8aab937..47674395309 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+@@ -37,7 +37,7 @@ bar (int i)
+ 
+ /* { 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\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler {\tpause} } } */
+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 a08022db8e4..e8cdc4fa05d 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
+@@ -16,7 +16,7 @@ male_indirect_jump (long 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\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler {\tpause} } } */
+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 b257c695ad1..5f333d86e18 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
+@@ -14,7 +14,7 @@ male_indirect_jump (long 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\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler {\tpause} } } */
+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 4bb1c5f9220..fa067b0acc8 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
+@@ -18,5 +18,5 @@ male_indirect_jump (long offset)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_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
+index 4e33a638862..442f97c123c 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
+@@ -17,5 +17,5 @@ male_indirect_jump (long offset)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_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
+index 427ba3ddbb4..64b85fc7b96 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
+@@ -37,7 +37,7 @@ bar (int i)
+ }
+ 
+ /* { 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_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" } } */
+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 5c20a35ecec..cb95a09ea34 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
+@@ -13,7 +13,7 @@ male_indirect_jump (long 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\]*__x86_indirect_thunk_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
+index b2fb6e1bcd2..b2af9e96b7e 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
+@@ -13,7 +13,7 @@ male_indirect_jump (long 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\]*__x86_indirect_thunk_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
+index 9c84547cd7c..a9a1a8bc177 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
+@@ -16,5 +16,5 @@ male_indirect_jump (long offset)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_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
+index 457849564bb..bbf3dd24f37 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
+@@ -16,5 +16,5 @@ male_indirect_jump (long offset)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { 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
+index d4747ea0764..39acad399d3 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
+@@ -37,7 +37,7 @@ bar (int i)
+ 
+ /* { 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\]*__x86_indirect_thunk_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-register-1.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
+index 7d396a31953..0660feeed73 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
+@@ -11,7 +11,7 @@ male_indirect_jump (long offset)
+   dispatch(offset);
+ }
+ 
+-/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } 
*/
++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_ax" } } */
+ /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+ /* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } 
*/
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
+index 5320e923be2..d39e387586e 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
+@@ -11,7 +11,7 @@ male_indirect_jump (long offset)
+   dispatch(offset);
+ }
+ 
+-/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } 
*/
++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_ax" } } */
+ /* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch"  } } */
+ /* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
+ /* { dg-final { scan-assembler-not {\t(pause|pause|nop)} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
+index b4f9d48065d..2f373c362d0 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
+@@ -18,6 +18,6 @@ foo (void)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_ax" { target 
{ x32 } }  } } */
++/* { dg-final { scan-assembler "__x86_indirect_thunk_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
+index 0312577a043..11e041fda7e 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
+@@ -18,6 +18,6 @@ foo (void)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_ax" { target 
{ x32 } }  } } */
++/* { dg-final { scan-assembler "__x86_indirect_thunk_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
+index fa3181303c9..f16c1a5d0c6 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
+@@ -17,6 +17,6 @@ foo (void)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_ax" { target 
{ x32 } }  } } */
++/* { dg-final { scan-assembler "__x86_indirect_thunk_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
+index 7a08e71c76b..b4553216f99 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
+@@ -18,5 +18,5 @@ foo (void)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_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
+index dacf0c769fc..28e5434a004 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
+@@ -18,5 +18,5 @@ foo (void)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_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
+index cf06a5f35c7..20fad48b790 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
+@@ -18,5 +18,5 @@ foo (void)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_ax" { target 
x32 } } } */
+ /* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+index 6da5ab97081..8f0375b5def 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+@@ -21,5 +21,5 @@ foo (void)
+ /* { 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 "call\[ \t\]*__x86_indirect_thunk_ax" { target 
{ x32 } } } } */
+ /* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
+-- 
+2.15.1
+
diff --git 
a/gnu/packages/patches/gcc-retpoline-x86-Add-V-register-operand-modifier.patch 
b/gnu/packages/patches/gcc-retpoline-x86-Add-V-register-operand-modifier.patch
new file mode 100644
index 000000000..d47f23942
--- /dev/null
+++ 
b/gnu/packages/patches/gcc-retpoline-x86-Add-V-register-operand-modifier.patch
@@ -0,0 +1,143 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From 5e977dfedb93e764dc480c0e0674500590ef5604 Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <address@hidden>
+Date: Sat, 6 Jan 2018 22:29:56 -0800
+Subject: [PATCH 07/10] x86: Add 'V' register operand modifier
+
+Add 'V', a special modifier which prints the name of the full integer
+register without '%'.  For
+
+extern void (*func_p) (void);
+
+void
+foo (void)
+{
+  asm ("call __x86_indirect_thunk_%V0" : : "a" (func_p));
+}
+
+it generates:
+
+foo:
+       movq    func_p(%rip), %rax
+       call    __x86_indirect_thunk_rax
+       ret
+
+gcc/
+
+       Backport from mainline
+       * config/i386/i386.c (print_reg): Print the name of the full
+       integer register without '%'.
+       (ix86_print_operand): Handle 'V'.
+        * doc/extend.texi: Document 'V' modifier.
+
+gcc/testsuite/
+
+       Backport from mainline
+       * gcc.target/i386/indirect-thunk-register-4.c: New test.
+---
+ gcc/config/i386/i386.c                                    | 13 ++++++++++++-
+ gcc/doc/extend.texi                                       |  3 +++
+ gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c | 13 +++++++++++++
+ 3 files changed, 28 insertions(+), 1 deletion(-)
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index 4bfe2fa8c1d..e32de13688a 100644
+--- a/gcc/config/i386/i386.c
++++ b/gcc/config/i386/i386.c
+@@ -17925,6 +17925,7 @@ put_condition_code (enum rtx_code code, machine_mode 
mode, bool reverse,
+    If CODE is 'h', pretend the reg is the 'high' byte register.
+    If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op.
+    If CODE is 'd', duplicate the operand for AVX instruction.
++   If CODE is 'V', print naked full integer register name without %.
+  */
+ 
+ void
+@@ -17935,7 +17936,7 @@ print_reg (rtx x, int code, FILE *file)
+   unsigned int regno;
+   bool duplicated;
+ 
+-  if (ASSEMBLER_DIALECT == ASM_ATT)
++  if (ASSEMBLER_DIALECT == ASM_ATT && code != 'V')
+     putc ('%', file);
+ 
+   if (x == pc_rtx)
+@@ -17983,6 +17984,14 @@ print_reg (rtx x, int code, FILE *file)
+       return;
+     }
+ 
++  if (code == 'V')
++    {
++      if (GENERAL_REGNO_P (regno))
++      msize = GET_MODE_SIZE (word_mode);
++      else
++      error ("'V' modifier on non-integer register");
++    }
++
+   duplicated = code == 'd' && TARGET_AVX;
+ 
+   switch (msize)
+@@ -18102,6 +18111,7 @@ print_reg (rtx x, int code, FILE *file)
+    & -- print some in-use local-dynamic symbol name.
+    H -- print a memory address offset by 8; used for sse high-parts
+    Y -- print condition for XOP pcom* instruction.
++   V -- print naked full integer register name without %.
+    + -- print a branch hint as 'cs' or 'ds' prefix
+    ; -- print a semicolon (after prefixes due to bug in older gas).
+    ~ -- print "i" if TARGET_AVX2, "f" otherwise.
+@@ -18326,6 +18336,7 @@ ix86_print_operand (FILE *file, rtx x, int code)
+       case 'X':
+       case 'P':
+       case 'p':
++      case 'V':
+         break;
+ 
+       case 's':
+diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
+index 46e0a3623a6..9db9e0e27e9 100644
+--- a/gcc/doc/extend.texi
++++ b/gcc/doc/extend.texi
+@@ -8778,6 +8778,9 @@ The table below shows the list of supported modifiers 
and their effects.
+ @tab @code{2}
+ @end multitable
+ 
address@hidden is a special modifier which prints the name of the full integer
++register without @code{%}.
++
+ @anchor{x86floatingpointasmoperands}
+ @subsubsection x86 Floating-Point @code{asm} Operands
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
+new file mode 100644
+index 00000000000..f0cd9b75be8
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
+@@ -0,0 +1,13 @@
++/* { dg-do compile } */
++/* { dg-options "-O2 -mindirect-branch=keep -fno-pic" } */
++
++extern void (*func_p) (void);
++
++void
++foo (void)
++{
++  asm("call __x86_indirect_thunk_%V0" : : "a" (func_p));
++}
++
++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_eax" { 
target ia32 } } } */
++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_rax" { 
target { ! ia32 } } } } */
+-- 
+2.15.1
+
diff --git a/gnu/packages/patches/gcc-retpoline-x86-Add-mfunction-return.patch 
b/gnu/packages/patches/gcc-retpoline-x86-Add-mfunction-return.patch
new file mode 100644
index 000000000..bfd154eaa
--- /dev/null
+++ b/gnu/packages/patches/gcc-retpoline-x86-Add-mfunction-return.patch
@@ -0,0 +1,1303 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From dfa5f37da1fed9d9439e396fdf49847f4d9184d4 Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <address@hidden>
+Date: Sat, 6 Jan 2018 22:29:56 -0800
+Subject: [PATCH 05/10] x86: Add -mfunction-return=
+
+Add -mfunction-return= option to convert function return to call and
+return thunks.  The default is 'keep', which keeps function return
+unmodified.  'thunk' converts function return to call and return thunk.
+'thunk-inline' converts function return to inlined call and return thunk.
+'thunk-extern' converts function return to external call and return
+thunk provided in a separate object file.  You can control this behavior
+for a specific function by using the function attribute function_return.
+
+Function return thunk is the same as memory thunk for -mindirect-branch=
+where the return address is at the top of the stack:
+
+__x86_return_thunk:
+       call L2
+L1:
+       pause
+       lfence
+       jmp L1
+L2:
+       lea 8(%rsp), %rsp|lea 4(%esp), %esp
+       ret
+
+and function return becomes
+
+       jmp __x86_return_thunk
+
+-mindirect-branch= tests are updated with -mfunction-return=keep to
+avoid false test failures when -mfunction-return=thunk is added to
+RUNTESTFLAGS for "make check".
+
+gcc/
+
+       Backport from mainline
+       * 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/
+
+       Backport from mainline
+       * 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.
+---
+ gcc/config/i386/i386-protos.h                      |   1 +
+ gcc/config/i386/i386.c                             | 151 +++++++++++++++++++--
+ gcc/config/i386/i386.h                             |   3 +
+ gcc/config/i386/i386.md                            |   9 +-
+ gcc/config/i386/i386.opt                           |   6 +-
+ gcc/doc/extend.texi                                |   9 ++
+ gcc/doc/invoke.texi                                |  13 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-1.c   |   2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-2.c   |   2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-3.c   |   2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-4.c   |   2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-5.c   |   2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-6.c   |   2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-7.c   |   2 +-
+ .../gcc.target/i386/indirect-thunk-attr-1.c        |   2 +-
+ .../gcc.target/i386/indirect-thunk-attr-2.c        |   2 +-
+ .../gcc.target/i386/indirect-thunk-attr-3.c        |   2 +-
+ .../gcc.target/i386/indirect-thunk-attr-4.c        |   2 +-
+ .../gcc.target/i386/indirect-thunk-attr-5.c        |   2 +-
+ .../gcc.target/i386/indirect-thunk-attr-6.c        |   2 +-
+ .../gcc.target/i386/indirect-thunk-attr-7.c        |   2 +-
+ .../gcc.target/i386/indirect-thunk-attr-8.c        |   2 +-
+ .../gcc.target/i386/indirect-thunk-bnd-1.c         |   2 +-
+ .../gcc.target/i386/indirect-thunk-bnd-2.c         |   2 +-
+ .../gcc.target/i386/indirect-thunk-bnd-3.c         |   2 +-
+ .../gcc.target/i386/indirect-thunk-bnd-4.c         |   2 +-
+ .../gcc.target/i386/indirect-thunk-extern-1.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-extern-2.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-extern-3.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-extern-4.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-extern-5.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-extern-6.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-extern-7.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-inline-1.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-inline-2.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-inline-3.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-inline-4.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-inline-5.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-inline-6.c      |   2 +-
+ .../gcc.target/i386/indirect-thunk-inline-7.c      |   2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-1.c        |  13 ++
+ gcc/testsuite/gcc.target/i386/ret-thunk-10.c       |  23 ++++
+ gcc/testsuite/gcc.target/i386/ret-thunk-11.c       |  23 ++++
+ gcc/testsuite/gcc.target/i386/ret-thunk-12.c       |  22 +++
+ gcc/testsuite/gcc.target/i386/ret-thunk-13.c       |  22 +++
+ gcc/testsuite/gcc.target/i386/ret-thunk-14.c       |  22 +++
+ gcc/testsuite/gcc.target/i386/ret-thunk-15.c       |  22 +++
+ gcc/testsuite/gcc.target/i386/ret-thunk-16.c       |  18 +++
+ gcc/testsuite/gcc.target/i386/ret-thunk-2.c        |  13 ++
+ gcc/testsuite/gcc.target/i386/ret-thunk-3.c        |  12 ++
+ gcc/testsuite/gcc.target/i386/ret-thunk-4.c        |  12 ++
+ gcc/testsuite/gcc.target/i386/ret-thunk-5.c        |  15 ++
+ gcc/testsuite/gcc.target/i386/ret-thunk-6.c        |  14 ++
+ gcc/testsuite/gcc.target/i386/ret-thunk-7.c        |  13 ++
+ gcc/testsuite/gcc.target/i386/ret-thunk-8.c        |  14 ++
+ gcc/testsuite/gcc.target/i386/ret-thunk-9.c        |  25 ++++
+ 56 files changed, 491 insertions(+), 50 deletions(-)
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-1.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-10.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-11.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-12.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-13.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-14.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-15.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-16.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-2.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-3.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-4.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-5.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-6.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-7.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-8.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+
+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 cd2e73cf9d3..d112bdb8552 100644
+--- a/gcc/config/i386/i386.md
++++ b/gcc/config/i386/i386.md
+@@ -12315,7 +12315,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")
+@@ -12337,12 +12337,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.
++
address@hidden function_return("@var{choice}")
address@hidden @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
+ address@hidden  address@hidden @gol
+ -mmitigate-rop  -mgeneral-regs-only @gol
address@hidden
address@hidden address@hidden
+ 
+ @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}.
+ 
address@hidden address@hidden
address@hidden -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}.
address@hidden 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 } } } */
+-- 
+2.15.1
+
diff --git 
a/gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch-register.patch 
b/gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch-register.patch
new file mode 100644
index 000000000..5fbced669
--- /dev/null
+++ b/gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch-register.patch
@@ -0,0 +1,907 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From 139dd2c61a11430263f91030910e2b63a73a11e7 Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <address@hidden>
+Date: Sat, 6 Jan 2018 22:29:56 -0800
+Subject: [PATCH 06/10] x86: Add -mindirect-branch-register
+
+Add -mindirect-branch-register to force indirect branch via register.
+This is implemented by disabling patterns of indirect branch via memory,
+similar to TARGET_X32.
+
+-mindirect-branch= and -mfunction-return= tests are updated with
+-mno-indirect-branch-register to avoid false test failures when
+-mindirect-branch-register is added to RUNTESTFLAGS for "make check".
+
+gcc/
+
+       Backport from mainline
+       * config/i386/constraints.md (Bs): Disallow memory operand for
+       -mindirect-branch-register.
+       (Bw): Likewise.
+       * config/i386/predicates.md (indirect_branch_operand): Likewise.
+       (GOT_memory_operand): Likewise.
+       (call_insn_operand): Likewise.
+       (sibcall_insn_operand): Likewise.
+       (GOT32_symbol_operand): Likewise.
+       * config/i386/i386.md (indirect_jump): Call convert_memory_address
+       for -mindirect-branch-register.
+       (tablejump): Likewise.
+       (*sibcall_memory): Likewise.
+       (*sibcall_value_memory): Likewise.
+       Disallow peepholes of indirect call and jump via memory for
+       -mindirect-branch-register.
+       (*call_pop): Replace m with Bw.
+       (*call_value_pop): Likewise.
+       (*sibcall_pop_memory): Replace m with Bs.
+       * config/i386/i386.opt (mindirect-branch-register): New option.
+       * doc/invoke.texi: Document -mindirect-branch-register option.
+
+gcc/testsuite/
+
+       Backport from mainline
+       * 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.
+---
+ gcc/config/i386/constraints.md                     | 12 +++++---
+ gcc/config/i386/i386.md                            | 34 ++++++++++++++--------
+ gcc/config/i386/i386.opt                           |  4 +++
+ gcc/config/i386/predicates.md                      | 21 ++++++++-----
+ gcc/doc/invoke.texi                                |  7 ++++-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-1.c   |  2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-2.c   |  2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-3.c   |  2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-4.c   |  2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-5.c   |  2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-6.c   |  2 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-7.c   |  2 +-
+ .../gcc.target/i386/indirect-thunk-attr-1.c        |  2 +-
+ .../gcc.target/i386/indirect-thunk-attr-2.c        |  2 +-
+ .../gcc.target/i386/indirect-thunk-attr-3.c        |  2 +-
+ .../gcc.target/i386/indirect-thunk-attr-4.c        |  2 +-
+ .../gcc.target/i386/indirect-thunk-attr-5.c        |  2 +-
+ .../gcc.target/i386/indirect-thunk-attr-6.c        |  2 +-
+ .../gcc.target/i386/indirect-thunk-attr-7.c        |  2 +-
+ .../gcc.target/i386/indirect-thunk-bnd-1.c         |  2 +-
+ .../gcc.target/i386/indirect-thunk-bnd-2.c         |  2 +-
+ .../gcc.target/i386/indirect-thunk-bnd-3.c         |  2 +-
+ .../gcc.target/i386/indirect-thunk-bnd-4.c         |  2 +-
+ .../gcc.target/i386/indirect-thunk-extern-1.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-extern-2.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-extern-3.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-extern-4.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-extern-5.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-extern-6.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-extern-7.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-inline-1.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-inline-2.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-inline-3.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-inline-4.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-inline-5.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-inline-6.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-inline-7.c      |  2 +-
+ .../gcc.target/i386/indirect-thunk-register-1.c    | 22 ++++++++++++++
+ .../gcc.target/i386/indirect-thunk-register-2.c    | 20 +++++++++++++
+ .../gcc.target/i386/indirect-thunk-register-3.c    | 19 ++++++++++++
+ gcc/testsuite/gcc.target/i386/ret-thunk-10.c       |  2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-11.c       |  2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-12.c       |  2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-13.c       |  2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-14.c       |  2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-15.c       |  2 +-
+ gcc/testsuite/gcc.target/i386/ret-thunk-9.c        |  2 +-
+ 47 files changed, 154 insertions(+), 63 deletions(-)
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
+
+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 d112bdb8552..96941e8c5c4 100644
+--- a/gcc/config/i386/i386.md
++++ b/gcc/config/i386/i386.md
+@@ -11625,7 +11625,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;
+ })
+@@ -11679,7 +11679,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;
+ })
+@@ -11871,7 +11871,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")])
+ 
+@@ -11880,7 +11880,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))
+@@ -11893,7 +11895,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)
+@@ -11915,7 +11919,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)
+@@ -11935,7 +11939,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)
+@@ -11989,7 +11993,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))])
+ 
+ ;; Call subroutine, returning value in operand 0
+@@ -12070,7 +12076,7 @@
+       (call (mem:QI (match_operand:W 1 "memory_operand" "m"))
+             (match_operand 2)))
+    (unspec [(const_int 0)] UNSPEC_PEEPSIB)]
+-  "!TARGET_X32"
++  "!TARGET_X32 && !ix86_indirect_branch_thunk_register"
+   "* return ix86_output_call_insn (insn, operands[1]);"
+   [(set_attr "type" "callv")])
+ 
+@@ -12080,7 +12086,9 @@
+    (set (match_operand 2)
+    (call (mem:QI (match_dup 0))
+                (match_operand 3)))]
+-  "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1))
++  "!TARGET_X32
++   && !ix86_indirect_branch_thunk_register
++   && SIBLING_CALL_P (peep2_next_insn (1))
+    && !reg_mentioned_p (operands[0],
+                       CALL_INSN_FUNCTION_USAGE (peep2_next_insn (1)))"
+   [(parallel [(set (match_dup 2)
+@@ -12095,7 +12103,9 @@
+    (set (match_operand 2)
+       (call (mem:QI (match_dup 0))
+             (match_operand 3)))]
+-  "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2))
++  "!TARGET_X32
++   && !ix86_indirect_branch_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)
+@@ -12120,7 +12130,7 @@
+ 
+ (define_insn "*call_value_pop"
+   [(set (match_operand 0)
+-      (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lmBz"))
++      (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lBwBz"))
+             (match_operand 2)))
+    (set (reg:SI SP_REG)
+       (plus:SI (reg:SI SP_REG)
+diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
+index b07388d95a9..852033cbb67 100644
+--- a/gcc/config/i386/i386.opt
++++ b/gcc/config/i386/i386.opt
+@@ -951,3 +951,7 @@ Enum(indirect_branch) String(thunk-inline) 
Value(indirect_branch_thunk_inline)
+ 
+ EnumValue
+ Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
++
++mindirect-branch-register
++Target Report Var(ix86_indirect_branch_thunk_register) Init(0)
++Force indirect call and jump via register.
+diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
+index 2fc2c60f6ac..a88b1d860ca 100644
+--- a/gcc/config/i386/predicates.md
++++ b/gcc/config/i386/predicates.md
+@@ -635,7 +635,8 @@
+ ;; Test for a valid operand for indirect branch.
+ (define_predicate "indirect_branch_operand"
+   (ior (match_operand 0 "register_operand")
+-       (and (not (match_test "TARGET_X32"))
++       (and (not (match_test "TARGET_X32
++                            || ix86_indirect_branch_thunk_register"))
+           (match_operand 0 "memory_operand"))))
+ 
+ ;; Return true if OP is a memory operands that can be used in sibcalls.
+@@ -664,7 +665,8 @@
+ 
+ ;; Return true if OP is a GOT memory operand.
+ (define_predicate "GOT_memory_operand"
+-  (match_operand 0 "memory_operand")
++  (and (match_test "!ix86_indirect_branch_thunk_register")
++       (match_operand 0 "memory_operand"))
+ {
+   op = XEXP (op, 0);
+   return (GET_CODE (op) == CONST
+@@ -678,9 +680,11 @@
+   (ior (match_test "constant_call_address_operand
+                    (op, mode == VOIDmode ? mode : Pmode)")
+        (match_operand 0 "call_register_no_elim_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")))))
+ 
+ ;; Similarly, but for tail calls, in which we cannot allow memory references.
+@@ -688,14 +692,17 @@
+   (ior (match_test "constant_call_address_operand
+                    (op, mode == VOIDmode ? mode : Pmode)")
+        (match_operand 0 "register_no_elim_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")))))
+ 
+ ;; Return true if OP is a 32-bit GOT symbol operand.
+ (define_predicate "GOT32_symbol_operand"
+-  (match_test "GET_CODE (op) == CONST
++  (match_test "!ix86_indirect_branch_thunk_register
++             && GET_CODE (op) == CONST
+                && GET_CODE (XEXP (op, 0)) == UNSPEC
+                && XINT (XEXP (op, 0), 1) == UNSPEC_GOT"))
+ 
+diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
+index f3eb54b1668..1e572b1f9a2 100644
+--- a/gcc/doc/invoke.texi
++++ b/gcc/doc/invoke.texi
+@@ -1211,7 +1211,8 @@ See RS/6000 and PowerPC Options.
+ -mavx256-split-unaligned-load  -mavx256-split-unaligned-store @gol
+ address@hidden  address@hidden @gol
+ -mmitigate-rop  -mgeneral-regs-only @gol
address@hidden address@hidden
address@hidden address@hidden @gol
++-mindirect-branch-register}
+ 
+ @emph{x86 Windows Options}
+ @gccoptlist{-mconsole  -mcygwin  -mno-cygwin  -mdll @gol
+@@ -25709,6 +25710,10 @@ object file.  You can control this behavior for a 
specific function by
+ using the function attribute @code{function_return}.
+ @xref{Function Attributes}.
+ 
address@hidden -mindirect-branch-register
address@hidden -mindirect-branch-register
++Force indirect call and jump via register.
++
+ @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 f076155c91a..9eb9b273ade 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 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" 
} */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+index d7984f592fe..c63795e4127 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 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" 
} */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+index 3257d0a2e16..82973cda771 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 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" 
} */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -mno-indirect-branch-register 
-mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+index 7cab2df6474..a5f3d1cbed8 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 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" 
} */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -mno-indirect-branch-register 
-mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+index b4836c38d6c..fcaa18d10b7 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 -mfunction-return=keep -fpic -fno-plt 
-mindirect-branch=thunk" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 1f06bd1af74..e4649283d10 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 -mfunction-return=keep -fpic -fno-plt 
-mindirect-branch=thunk" } */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -mno-indirect-branch-register 
-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 0b3fef86a20..ebfb8aab937 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 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" 
} */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk -fno-pic" } */
+ 
+ void func0 (void);
+ void func1 (void);
+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 5f6cfc17b56..a08022db8e4 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 -mfunction-return=keep -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 b256160ec80..b257c695ad1 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 -mfunction-return=keep -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 567c95051d6..dfb1370d23d 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 -mfunction-return=keep -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 3b662af7d5d..a6e3f6f9f2b 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 -mfunction-return=keep -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 98785a38248..4bb1c5f9220 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 -mfunction-return=keep -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 a498a39e404..4e33a638862 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 -mfunction-return=keep -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 66f295d1eb6..427ba3ddbb4 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 -mfunction-return=keep -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-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 aacb814d737..dc7143414fb 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 -mfunction-return=keep -mindirect-branch=thunk 
-fcheck-pointer-bounds -mmpx -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+ 
+ void (*dispatch) (char *);
+ char buf[10];
+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 7b44dda23df..737c60946f6 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 -mfunction-return=keep -mindirect-branch=thunk 
-fcheck-pointer-bounds -mmpx -fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+ 
+ void (*dispatch) (char *);
+ char buf[10];
+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 70b4fb36eea..d34485a0010 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 -mfunction-return=keep -mindirect-branch=thunk 
-fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 3baf03ee77c..0e19830de4d 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 -mfunction-return=keep -mindirect-branch=thunk 
-fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -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 637fc3d3f4e..5c20a35ecec 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 -mfunction-return=keep -mindirect-branch=thunk-extern 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-extern -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+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 ff9efe03fe6..b2fb6e1bcd2 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 -mfunction-return=keep -mindirect-branch=thunk-extern 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-extern -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+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 2686a5f2db4..9c84547cd7c 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 -mfunction-return=keep -mindirect-branch=thunk-extern 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-extern -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+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 f07f6b214ad..457849564bb 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 -mfunction-return=keep -mindirect-branch=thunk-extern 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-extern -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+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 21740ac5b7f..5c07e02df6a 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 -mfunction-return=keep -fpic -fno-plt 
-mindirect-branch=thunk-extern" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 a77c1f470b8..3eb440693a0 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 -mfunction-return=keep -fpic -fno-plt 
-mindirect-branch=thunk-extern" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 e64910fd4aa..d4747ea0764 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 -mfunction-return=keep -mindirect-branch=thunk-extern 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-extern -fno-pic" } */
+ 
+ void func0 (void);
+ void func1 (void);
+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 365cf2ee226..536abfa74e4 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 -mfunction-return=keep -mindirect-branch=thunk-inline 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-inline -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+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 72646a4960b..bd2b6246aa1 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 -mfunction-return=keep -mindirect-branch=thunk-inline 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-inline -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+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 f48945e3dfc..9885eebbcff 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 -mfunction-return=keep -mindirect-branch=thunk-inline 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-inline -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+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 4b1d558fc4e..7b3983949d2 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 -mfunction-return=keep -mindirect-branch=thunk-inline 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-inline -fno-pic" } */
+ 
+ typedef void (*dispatch_t)(long offset);
+ 
+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 0f687c3b027..c6d77e10352 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 -mfunction-return=keep -fpic -fno-plt 
-mindirect-branch=thunk-inline" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 b27c6fc96a2..6454827b780 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 -mfunction-return=keep -fpic -fno-plt 
-mindirect-branch=thunk-inline" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -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 2c496492eaa..cc592f89aba 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 -mfunction-return=keep -mindirect-branch=thunk-inline 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-inline -fno-pic" } */
+ 
+ void func0 (void);
+ void func1 (void);
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
+new file mode 100644
+index 00000000000..7d396a31953
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
+@@ -0,0 +1,22 @@
++/* { dg-do compile } */
++/* { dg-options "-O2 -mindirect-branch=thunk -mindirect-branch-register 
-fno-pic" } */
++
++typedef void (*dispatch_t)(long offset);
++
++dispatch_t dispatch;
++
++void
++male_indirect_jump (long offset)
++{
++  dispatch(offset);
++}
++
++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } 
*/
++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
++/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } 
*/
++/* { dg-final { scan-assembler {\tpause} } } */
++/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch"  } } */
++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
++/* { dg-final { scan-assembler-not "__x86_indirect_thunk\n" } } */
++/* { dg-final { scan-assembler-not "__x86_indirect_thunk_bnd\n" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
+new file mode 100644
+index 00000000000..e7e616bb271
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
+@@ -0,0 +1,20 @@
++/* { dg-do compile } */
++/* { dg-options "-O2 -mindirect-branch=thunk-inline 
-mindirect-branch-register -fno-pic" } */
++
++typedef void (*dispatch_t)(long offset);
++
++dispatch_t dispatch;
++
++void
++male_indirect_jump (long offset)
++{
++  dispatch(offset);
++}
++
++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
++/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } 
*/
++/* { dg-final { scan-assembler {\tpause} } } */
++/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch"  } } */
++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
+new file mode 100644
+index 00000000000..5320e923be2
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
+@@ -0,0 +1,19 @@
++/* { dg-do compile } */
++/* { dg-options "-O2 -mindirect-branch=thunk-extern 
-mindirect-branch-register -fno-pic" } */
++
++typedef void (*dispatch_t)(long offset);
++
++dispatch_t dispatch;
++
++void
++male_indirect_jump (long offset)
++{
++  dispatch(offset);
++}
++
++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } 
*/
++/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch"  } } */
++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
++/* { dg-final { scan-assembler-not {\t(pause|pause|nop)} } } */
++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
+index 1630e2fa2b5..b4f9d48065d 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile } */
+-/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -mfunction-return=thunk-inline 
-mindirect-branch=thunk -fno-pic" } */
+ 
+ extern void (*bar) (void);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
+index 876159cf783..0312577a043 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile } */
+-/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -mno-indirect-branch-register 
-mno-indirect-branch-register -mfunction-return=thunk-extern 
-mindirect-branch=thunk -fno-pic" } */
+ 
+ extern void (*bar) (void);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
+index 01b0a02f80b..fa3181303c9 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile } */
+-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" 
} */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -mno-indirect-branch-register 
-mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk 
-fno-pic" } */
+ 
+ extern void (*bar) (void);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
+index e028c2b6a99..7a08e71c76b 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile } */
+-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-inline -fno-pic" } */
+ 
+ extern void (*bar) (void);
+ extern int foo (void) __attribute__ ((function_return("thunk")));
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
+index c14ee3ae4c0..dacf0c769fc 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile } */
+-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep 
-mindirect-branch=thunk-extern -fno-pic" } */
+ 
+ extern void (*bar) (void);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
+index 2f21e138ec2..cf06a5f35c7 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile } */
+-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" 
} */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -mno-indirect-branch-register 
-mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=keep 
-fno-pic" } */
+ 
+ extern void (*bar) (void);
+ 
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+index f6ccad98da7..6da5ab97081 100644
+--- a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile } */
+-/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk 
-fno-pic" } */
++/* { dg-options "-O2 -mno-indirect-branch-register 
-mno-indirect-branch-register -mfunction-return=thunk -mindirect-branch=thunk 
-fno-pic" } */
+ 
+ extern void (*bar) (void);
+ 
+-- 
+2.15.1
+
diff --git a/gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch.patch 
b/gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch.patch
new file mode 100644
index 000000000..e622601ea
--- /dev/null
+++ b/gnu/packages/patches/gcc-retpoline-x86-Add-mindirect-branch.patch
@@ -0,0 +1,2171 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From 09f7c546376f7ed6770fc64f24aed77229f95f67 Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <address@hidden>
+Date: Sat, 6 Jan 2018 22:29:55 -0800
+Subject: [PATCH 04/10] x86: Add -mindirect-branch=
+
+Add -mindirect-branch= option to convert indirect call and jump to call
+and return thunks.  The default is 'keep', which keeps indirect call and
+jump unmodified.  'thunk' converts indirect call and jump to call and
+return thunk.  'thunk-inline' converts indirect call and jump to inlined
+call and return thunk.  'thunk-extern' converts indirect call and jump to
+external call and return thunk provided in a separate object file.  You
+can control this behavior for a specific function by using the function
+attribute indirect_branch.
+
+2 kinds of thunks are geneated.  Memory thunk where the function address
+is at the top of the stack:
+
+__x86_indirect_thunk:
+       call L2
+L1:
+       pause
+       lfence
+       jmp L1
+L2:
+       lea 8(%rsp), %rsp|lea 4(%esp), %esp
+       ret
+
+Indirect jmp via memory, "jmp mem", is converted to
+
+       push memory
+       jmp __x86_indirect_thunk
+
+Indirect call via memory, "call mem", is converted to
+
+       jmp L2
+L1:
+       push [mem]
+       jmp __x86_indirect_thunk
+L2:
+       call L1
+
+Register thunk where the function address is in a register, reg:
+
+__x86_indirect_thunk_reg:
+       call    L2
+L1:
+       pause
+       lfence
+       jmp     L1
+L2:
+       movq    %reg, (%rsp)|movl    %reg, (%esp)
+       ret
+
+where reg is one of (r|e)ax, (r|e)dx, (r|e)cx, (r|e)bx, (r|e)si, (r|e)di,
+(r|e)bp, r8, r9, r10, r11, r12, r13, r14 and r15.
+
+Indirect jmp via register, "jmp reg", is converted to
+
+       jmp __x86_indirect_thunk_reg
+
+Indirect call via register, "call reg", is converted to
+
+       call __x86_indirect_thunk_reg
+
+gcc/
+
+       Backport from mainline
+       * config/i386/i386-opts.h (indirect_branch): New.
+       * config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
+       * config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
+       with local indirect jump when converting indirect call and jump.
+       (ix86_set_indirect_branch_type): New.
+       (ix86_set_current_function): Call ix86_set_indirect_branch_type.
+       (indirectlabelno): New.
+       (indirect_thunk_needed): Likewise.
+       (indirect_thunk_bnd_needed): Likewise.
+       (indirect_thunks_used): Likewise.
+       (indirect_thunks_bnd_used): Likewise.
+       (INDIRECT_LABEL): Likewise.
+       (indirect_thunk_name): Likewise.
+       (output_indirect_thunk): Likewise.
+       (output_indirect_thunk_function): Likewise.
+       (ix86_output_indirect_branch): Likewise.
+       (ix86_output_indirect_jmp): Likewise.
+       (ix86_code_end): Call output_indirect_thunk_function if needed.
+       (ix86_output_call_insn): Call ix86_output_indirect_branch if
+       needed.
+       (ix86_handle_fndecl_attribute): Handle indirect_branch.
+       (ix86_attribute_table): Add indirect_branch.
+       * config/i386/i386.h (machine_function): Add indirect_branch_type
+       and has_local_indirect_jump.
+       * config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
+       to true.
+       (tablejump): Likewise.
+       (*indirect_jump): Use ix86_output_indirect_jmp.
+       (*tablejump_1): Likewise.
+       (simple_return_indirect_internal): Likewise.
+       * config/i386/i386.opt (mindirect-branch=): New option.
+       (indirect_branch): New.
+       (keep): Likewise.
+       (thunk): Likewise.
+       (thunk-inline): Likewise.
+       (thunk-extern): Likewise.
+       * doc/extend.texi: Document indirect_branch function attribute.
+       * doc/invoke.texi: Document -mindirect-branch= option.
+
+gcc/testsuite/
+
+       Backport from mainline
+       * gcc.target/i386/indirect-thunk-1.c: New test.
+       * gcc.target/i386/indirect-thunk-2.c: Likewise.
+       * gcc.target/i386/indirect-thunk-3.c: Likewise.
+       * gcc.target/i386/indirect-thunk-4.c: Likewise.
+       * gcc.target/i386/indirect-thunk-5.c: Likewise.
+       * gcc.target/i386/indirect-thunk-6.c: Likewise.
+       * gcc.target/i386/indirect-thunk-7.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
+       * gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
+       * gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
+       * gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
+       * gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
+       * gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
+       * gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
+       * gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
+       * gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
+       * gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
+       * gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
+       * gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
+       * gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
+       * gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
+       * gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
+       * gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
+       * gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
+       * gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
+       * gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
+---
+ gcc/config/i386/i386-opts.h                        |  13 +
+ gcc/config/i386/i386-protos.h                      |   1 +
+ gcc/config/i386/i386.c                             | 648 ++++++++++++++++++++-
+ gcc/config/i386/i386.h                             |   7 +
+ gcc/config/i386/i386.md                            |  26 +-
+ gcc/config/i386/i386.opt                           |  20 +
+ gcc/doc/extend.texi                                |  10 +
+ gcc/doc/invoke.texi                                |  14 +-
+ gcc/testsuite/gcc.target/i386/indirect-thunk-1.c   |  20 +
+ gcc/testsuite/gcc.target/i386/indirect-thunk-2.c   |  20 +
+ gcc/testsuite/gcc.target/i386/indirect-thunk-3.c   |  21 +
+ gcc/testsuite/gcc.target/i386/indirect-thunk-4.c   |  21 +
+ gcc/testsuite/gcc.target/i386/indirect-thunk-5.c   |  17 +
+ gcc/testsuite/gcc.target/i386/indirect-thunk-6.c   |  18 +
+ gcc/testsuite/gcc.target/i386/indirect-thunk-7.c   |  44 ++
+ .../gcc.target/i386/indirect-thunk-attr-1.c        |  23 +
+ .../gcc.target/i386/indirect-thunk-attr-2.c        |  21 +
+ .../gcc.target/i386/indirect-thunk-attr-3.c        |  23 +
+ .../gcc.target/i386/indirect-thunk-attr-4.c        |  22 +
+ .../gcc.target/i386/indirect-thunk-attr-5.c        |  22 +
+ .../gcc.target/i386/indirect-thunk-attr-6.c        |  21 +
+ .../gcc.target/i386/indirect-thunk-attr-7.c        |  44 ++
+ .../gcc.target/i386/indirect-thunk-attr-8.c        |  42 ++
+ .../gcc.target/i386/indirect-thunk-bnd-1.c         |  20 +
+ .../gcc.target/i386/indirect-thunk-bnd-2.c         |  21 +
+ .../gcc.target/i386/indirect-thunk-bnd-3.c         |  19 +
+ .../gcc.target/i386/indirect-thunk-bnd-4.c         |  20 +
+ .../gcc.target/i386/indirect-thunk-extern-1.c      |  19 +
+ .../gcc.target/i386/indirect-thunk-extern-2.c      |  19 +
+ .../gcc.target/i386/indirect-thunk-extern-3.c      |  20 +
+ .../gcc.target/i386/indirect-thunk-extern-4.c      |  20 +
+ .../gcc.target/i386/indirect-thunk-extern-5.c      |  16 +
+ .../gcc.target/i386/indirect-thunk-extern-6.c      |  17 +
+ .../gcc.target/i386/indirect-thunk-extern-7.c      |  43 ++
+ .../gcc.target/i386/indirect-thunk-inline-1.c      |  20 +
+ .../gcc.target/i386/indirect-thunk-inline-2.c      |  20 +
+ .../gcc.target/i386/indirect-thunk-inline-3.c      |  21 +
+ .../gcc.target/i386/indirect-thunk-inline-4.c      |  21 +
+ .../gcc.target/i386/indirect-thunk-inline-5.c      |  17 +
+ .../gcc.target/i386/indirect-thunk-inline-6.c      |  18 +
+ .../gcc.target/i386/indirect-thunk-inline-7.c      |  44 ++
+ 41 files changed, 1494 insertions(+), 19 deletions(-)
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
+
+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 = "address@hidden(%%rip)|[QWORD PTR address@hidden";
++              {
++                if (output_indirect_p)
++                  xasm = "address@hidden(%%rip)|[QWORD PTR address@hidden";
++                else
++                  xasm = "address@hidden(%%rip)|[QWORD PTR address@hidden";
++              }
+             else
+-              xasm = "address@hidden|[DWORD PTR address@hidden";
++              {
++                if (output_indirect_p)
++                  xasm = "address@hidden|[DWORD PTR address@hidden";
++                else
++                  xasm = "address@hidden|[DWORD PTR address@hidden";
++              }
+           }
+         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 = "address@hidden(%%rip)|[QWORD PTR address@hidden";
++          {
++            if (output_indirect_p)
++              xasm = "address@hidden(%%rip)|[QWORD PTR address@hidden";
++            else
++              xasm = "address@hidden(%%rip)|[QWORD PTR address@hidden";
++          }
+         else
+-          xasm = "address@hidden|[DWORD PTR address@hidden";
++          {
++            if (output_indirect_p)
++              xasm = "address@hidden|[DWORD PTR address@hidden";
++            else
++              xasm = "address@hidden|[DWORD PTR address@hidden";
++          }
+       }
+       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 dbe88f40c8f..cd2e73cf9d3 100644
+--- a/gcc/config/i386/i386.md
++++ b/gcc/config/i386/i386.md
+@@ -11627,13 +11627,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")])
+ 
+@@ -11676,14 +11681,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")])
+
+@@ -12354,8 +12364,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.
++
address@hidden indirect_branch("@var{choice}")
address@hidden @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.
address@hidden 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
+ address@hidden  address@hidden @gol
+--mmitigate-rop  -mgeneral-regs-only}
++-mmitigate-rop  -mgeneral-regs-only @gol
address@hidden
+ 
+ @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.
+ 
address@hidden address@hidden
address@hidden -mindirect-branch
++Convert indirect call and jump with @var{choice}.  The default is
address@hidden, which keeps indirect call and jump unmodified.
address@hidden converts indirect call and jump to call and return thunk.
address@hidden 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)\[ address@hidden" } } */
++/* { 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)\[ address@hidden" } } */
++/* { 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)\[ address@hidden" } } */
++/* { 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)\[ address@hidden" } } */
++/* { 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)\[ address@hidden" } } */
++/* { 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)\[ address@hidden" } } */
++/* { 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)\[ address@hidden" } } */
++/* { 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)\[ address@hidden" } } */
++/* { 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" } } */
+-- 
+2.15.1
+
diff --git 
a/gnu/packages/patches/gcc-retpoline-x86-Disallow-mindirect-branch-mfunction-return-with-.patch
 
b/gnu/packages/patches/gcc-retpoline-x86-Disallow-mindirect-branch-mfunction-return-with-.patch
new file mode 100644
index 000000000..1dae0b493
--- /dev/null
+++ 
b/gnu/packages/patches/gcc-retpoline-x86-Disallow-mindirect-branch-mfunction-return-with-.patch
@@ -0,0 +1,308 @@
+'Retpoline' mitigation technique for Spectre (branch target injection)
+[CVE-2017-5715]:
+
+https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html
+https://support.google.com/faqs/answer/7625886
+https://spectreattack.com/
+https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715
+
+Patch copied from the 'retpoline-regnames' branch of upstream source repository
+(please keep an eye for new branches or updates for existing branches):
+
+http://git.infradead.org/users/dwmw2/gcc-retpoline.git
+
+From 13dce7cceef28026c4fc2e505d724526141fe4c1 Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <address@hidden>
+Date: Sat, 13 Jan 2018 18:01:54 -0800
+Subject: [PATCH 08/10] x86: Disallow -mindirect-branch=/-mfunction-return=
+ with -mcmodel=large
+
+Since the thunk function may not be reachable in large code model,
+-mcmodel=large is incompatible with -mindirect-branch=thunk,
+-mindirect-branch=thunk-extern, -mfunction-return=thunk and
+-mfunction-return=thunk-extern.  Issue an error when they are used with
+-mcmodel=large.
+
+gcc/
+
+       Backport from mainline
+       * config/i386/i386.c (ix86_set_indirect_branch_type): Disallow
+       -mcmodel=large with -mindirect-branch=thunk,
+       -mindirect-branch=thunk-extern, -mfunction-return=thunk and
+       -mfunction-return=thunk-extern.
+       * doc/invoke.texi: Document -mcmodel=large is incompatible with
+       -mindirect-branch=thunk, -mindirect-branch=thunk-extern,
+       -mfunction-return=thunk and -mfunction-return=thunk-extern.
+
+gcc/testsuite/
+
+       Backport from mainline
+       * gcc.target/i386/indirect-thunk-10.c: New test.
+       * gcc.target/i386/indirect-thunk-8.c: Likewise.
+       * gcc.target/i386/indirect-thunk-9.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-10.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-11.c: Likewise.
+       * gcc.target/i386/indirect-thunk-attr-9.c: Likewise.
+       * gcc.target/i386/ret-thunk-17.c: Likewise.
+       * gcc.target/i386/ret-thunk-18.c: Likewise.
+       * gcc.target/i386/ret-thunk-19.c: Likewise.
+       * gcc.target/i386/ret-thunk-20.c: Likewise.
+       * gcc.target/i386/ret-thunk-21.c: Likewise.
+---
+ gcc/config/i386/i386.c                             | 26 ++++++++++++++++++++++
+ gcc/doc/invoke.texi                                | 11 +++++++++
+ gcc/testsuite/gcc.target/i386/indirect-thunk-10.c  |  7 ++++++
+ gcc/testsuite/gcc.target/i386/indirect-thunk-8.c   |  7 ++++++
+ gcc/testsuite/gcc.target/i386/indirect-thunk-9.c   |  7 ++++++
+ .../gcc.target/i386/indirect-thunk-attr-10.c       |  9 ++++++++
+ .../gcc.target/i386/indirect-thunk-attr-11.c       |  9 ++++++++
+ .../gcc.target/i386/indirect-thunk-attr-9.c        |  9 ++++++++
+ gcc/testsuite/gcc.target/i386/ret-thunk-17.c       |  7 ++++++
+ gcc/testsuite/gcc.target/i386/ret-thunk-18.c       |  8 +++++++
+ gcc/testsuite/gcc.target/i386/ret-thunk-19.c       |  8 +++++++
+ gcc/testsuite/gcc.target/i386/ret-thunk-20.c       |  9 ++++++++
+ gcc/testsuite/gcc.target/i386/ret-thunk-21.c       |  9 ++++++++
+ 13 files changed, 126 insertions(+)
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-17.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-18.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-19.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-20.c
+ create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-21.c
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index e32de13688a..318a71840c9 100644
+--- a/gcc/config/i386/i386.c
++++ b/gcc/config/i386/i386.c
+@@ -7187,6 +7187,19 @@ ix86_set_indirect_branch_type (tree fndecl)
+       }
+       else
+       cfun->machine->indirect_branch_type = ix86_indirect_branch;
++
++      /* -mcmodel=large is not compatible with -mindirect-branch=thunk
++       nor -mindirect-branch=thunk-extern.  */
++      if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
++        && ((cfun->machine->indirect_branch_type
++             == indirect_branch_thunk_extern)
++            || (cfun->machine->indirect_branch_type
++                == indirect_branch_thunk)))
++      error ("%<-mindirect-branch=%s%> and %<-mcmodel=large%> are not "
++             "compatible",
++             ((cfun->machine->indirect_branch_type
++               == indirect_branch_thunk_extern)
++              ? "thunk-extern" : "thunk"));
+     }
+ 
+   if (cfun->machine->function_return_type == indirect_branch_unset)
+@@ -7212,6 +7225,19 @@ ix86_set_indirect_branch_type (tree fndecl)
+       }
+       else
+       cfun->machine->function_return_type = ix86_function_return;
++
++      /* -mcmodel=large is not compatible with -mfunction-return=thunk
++       nor -mfunction-return=thunk-extern.  */
++      if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
++        && ((cfun->machine->function_return_type
++             == indirect_branch_thunk_extern)
++            || (cfun->machine->function_return_type
++                == indirect_branch_thunk)))
++      error ("%<-mfunction-return=%s%> and %<-mcmodel=large%> are not "
++             "compatible",
++             ((cfun->machine->function_return_type
++               == indirect_branch_thunk_extern)
++              ? "thunk-extern" : "thunk"));
+     }
+ }
+ 
+diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
+index 1e572b1f9a2..6f3c344476c 100644
+--- a/gcc/doc/invoke.texi
++++ b/gcc/doc/invoke.texi
+@@ -25699,6 +25699,11 @@ to external call and return thunk provided in a 
separate object file.
+ You can control this behavior for a specific function by using the
+ function attribute @code{indirect_branch}.  @xref{Function Attributes}.
+ 
++Note that @option{-mcmodel=large} is incompatible with
address@hidden nor
address@hidden since the thunk function may
++not be reachable in large code model.
++
+ @item address@hidden
+ @opindex -mfunction-return
+ Convert function return with @var{choice}.  The default is @samp{keep},
+@@ -25710,6 +25715,12 @@ object file.  You can control this behavior for a 
specific function by
+ using the function attribute @code{function_return}.
+ @xref{Function Attributes}.
+ 
++Note that @option{-mcmodel=large} is incompatible with
address@hidden nor
address@hidden since the thunk function may
++not be reachable in large code model.
++
++
+ @item -mindirect-branch-register
+ @opindex -mindirect-branch-register
+ Force indirect call and jump via register.
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
+new file mode 100644
+index 00000000000..a0674bd2363
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
+@@ -0,0 +1,7 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mindirect-branch=thunk-inline -mfunction-return=keep 
-mcmodel=large" } */
++
++void
++bar (void)
++{
++}
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
+new file mode 100644
+index 00000000000..7a80a8986e8
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
+@@ -0,0 +1,7 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mindirect-branch=thunk -mfunction-return=keep 
-mcmodel=large" } */
++
++void
++bar (void)
++{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not 
compatible" } */
++}
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
+new file mode 100644
+index 00000000000..d4d45c5114d
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
+@@ -0,0 +1,7 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mindirect-branch=thunk-extern -mfunction-return=keep 
-mcmodel=large" } */
++
++void
++bar (void)
++{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are 
not compatible" } */
++}
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
+new file mode 100644
+index 00000000000..3a2aeaddbc5
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
+@@ -0,0 +1,9 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep 
-mcmodel=large" } */
++/* { dg-additional-options "-fPIC" { target fpic } } */
++
++__attribute__ ((indirect_branch("thunk-extern")))
++void
++bar (void)
++{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are 
not compatible" } */
++}
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
+new file mode 100644
+index 00000000000..8e52f032b6c
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
+@@ -0,0 +1,9 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep 
-mcmodel=large" } */
++/* { dg-additional-options "-fPIC" { target fpic } } */
++
++__attribute__ ((indirect_branch("thunk-inline")))
++void
++bar (void)
++{
++}
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c 
b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
+new file mode 100644
+index 00000000000..bdaa4f6911b
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
+@@ -0,0 +1,9 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep 
-mcmodel=large" } */
++/* { dg-additional-options "-fPIC" { target fpic } } */
++
++__attribute__ ((indirect_branch("thunk")))
++void
++bar (void)
++{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not 
compatible" } */
++}
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-17.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
+new file mode 100644
+index 00000000000..0605e2c6542
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
+@@ -0,0 +1,7 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=keep 
-mcmodel=large" } */
++
++void
++bar (void)
++{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not 
compatible" } */
++}
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-18.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
+new file mode 100644
+index 00000000000..307019dc242
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
+@@ -0,0 +1,8 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=keep 
-mcmodel=large" } */
++/* { dg-additional-options "-fPIC" { target fpic } } */
++
++void
++bar (void)
++{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are 
not compatible" } */
++}
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-19.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
+new file mode 100644
+index 00000000000..772617f4010
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
+@@ -0,0 +1,8 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep 
-mcmodel=large" } */
++
++__attribute__ ((function_return("thunk")))
++void
++bar (void)
++{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not 
compatible" } */
++}
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-20.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
+new file mode 100644
+index 00000000000..1e9f9bd5a66
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
+@@ -0,0 +1,9 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep 
-mcmodel=large" } */
++/* { dg-additional-options "-fPIC" { target fpic } } */
++
++__attribute__ ((function_return("thunk-extern")))
++void
++bar (void)
++{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are 
not compatible" } */
++}
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-21.c 
b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
+new file mode 100644
+index 00000000000..eea07f7abe1
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
+@@ -0,0 +1,9 @@
++/* { dg-do compile { target { lp64 } } } */
++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep 
-mcmodel=large" } */
++/* { dg-additional-options "-fPIC" { target fpic } } */
++
++__attribute__ ((function_return("thunk-inline")))
++void
++bar (void)
++{
++}
+-- 
+2.15.1
+
-- 
2.15.1

Cheers,
Alex

reply via email to

[Prev in Thread] Current Thread [Next in Thread]