Backport of following revs for Spectre v2 mitigation/retpolines:

- trunk@166119
- gcc-7-branch@256691
- gcc-7-branch@256692
- gcc-7-branch@256742
- trunk@r178388
- trunk@256660
- trunk@256661
- trunk@256662
- trunk@256663
- trunk@256664

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 8dd203ebee6..41d633ac992 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -258,3 +258,6 @@ extern enum rtx_code ix86_fp_compare_code_to_integer (enum rtx_code);
 extern rtx construct_plt_address (rtx);
 #endif
 extern int asm_preferred_eh_data_format (int, int);
+extern const char * ix86_output_call_insn (rtx insn, rtx call_op, int addr_op);
+extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
+extern const char * ix86_output_function_return (bool long_p);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index ad9548a7494..ff39d7273fd 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -1626,46 +1626,6 @@ struct stack_local_entry GTY(())
   struct stack_local_entry *next;
 };
 
-/* Structure describing stack frame layout.
-   Stack grows downward:
-
-   [arguments]
-					      <- ARG_POINTER
-   saved pc
-
-   saved frame pointer if frame_pointer_needed
-					      <- HARD_FRAME_POINTER
-   [saved regs]
-
-   [padding1]          \
-		        )
-   [va_arg registers]  (
-		        > to_allocate	      <- FRAME_POINTER
-   [frame]	       (
-		        )
-   [padding2]	       /
-  */
-struct ix86_frame
-{
-  int nregs;
-  int padding1;
-  int va_arg_size;
-  HOST_WIDE_INT frame;
-  int padding2;
-  int outgoing_arguments_size;
-  int red_zone_size;
-
-  HOST_WIDE_INT to_allocate;
-  /* 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;
-
-  /* When save_regs_using_mov is set, emit prologue using
-     move instead of push instructions.  */
-  bool save_regs_using_mov;
-};
-
 /* Code model option.  */
 enum cmodel ix86_cmodel;
 /* Asm dialect.  */
@@ -1747,9 +1707,10 @@ static bool ext_80387_constants_init = 0;
 static struct machine_function * ix86_init_machine_status (void);
 static rtx ix86_function_value (const_tree, 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, enum machine_mode,
 						 rtx, rtx, int);
+static void ix86_set_current_function (tree);
 
 
 /* The svr4 ABI for the i386 says that records and unions are returned
@@ -1829,6 +1790,45 @@ static int ix86_isa_flags_explicit;
 tree (*ix86_veclib_handler)(enum built_in_function, tree, tree) = NULL;
 static tree ix86_veclibabi_acml (enum built_in_function, tree, tree);
 
+/* Return true if a red-zone is in use.  We can't use red-zone when
+   there are local indirect jumps, like "indirect_jump" or "tablejump",
+   which jumps to another place in the function, since "call" in the
+   indirect thunk pushes the return address onto stack, destroying
+   red-zone.
+
+   TODO: If we can reserve the first 2 WORDs, for PUSH and, another
+   for CALL, in red-zone, we can allow local indirect jumps with
+   indirect thunk.  */
+
+static inline bool
+ix86_using_red_zone (void)
+{
+  return (TARGET_RED_ZONE
+	  && !TARGET_64BIT_MS_ABI
+	  && (!cfun->machine->has_local_indirect_jump
+	      || cfun->machine->indirect_branch_type == indirect_branch_keep));
+}
+
+/* Get indiret_branch enum value based on string VALUE.  */
+
+static enum indirect_branch
+get_indirect_branch_type_from_string (const char *value)
+{
+  if (value == NULL)
+    return indirect_branch_unset;
+
+  if (strcmp (value, "keep") == 0)
+    return indirect_branch_keep;
+  else if (strcmp (value, "thunk") == 0)
+    return indirect_branch_thunk;
+  else if (strcmp (value, "thunk-inline") == 0)
+    return indirect_branch_thunk_inline;
+  else if (strcmp (value, "thunk-extern") == 0)
+    return indirect_branch_thunk_extern;
+  else
+    return indirect_branch_unset;
+}
+
 /* Implement TARGET_HANDLE_OPTION.  */
 
 static bool
@@ -2711,6 +2711,24 @@ override_options (void)
       target_flags |= MASK_ACCUMULATE_OUTGOING_ARGS;
     }
 
+  if (ix86_indirect_branch)
+  {
+    enum indirect_branch value
+      = get_indirect_branch_type_from_string (ix86_indirect_branch);
+    if (value == indirect_branch_unset)
+      error ("bad value (%s) for -mindirect-branch= switch",
+	     ix86_indirect_branch);
+  }
+
+  if (ix86_function_return)
+  {
+    enum indirect_branch value
+      = get_indirect_branch_type_from_string (ix86_function_return);
+    if (value == indirect_branch_unset)
+      error ("bad value (%s) for -mfunction-return= switch",
+	     ix86_function_return);
+  }
+
   /* For sane SSE instruction set generation we need fcomi instruction.
      It is safe to enable all CMOVE instructions.  */
   if (TARGET_SSE)
@@ -5697,8 +5715,6 @@ symbolic_reference_mentioned_p (rtx op)
 int
 ix86_can_use_return_insn_p (void)
 {
-  struct ix86_frame frame;
-
   if (! reload_completed || frame_pointer_needed)
     return 0;
 
@@ -5708,7 +5724,8 @@ ix86_can_use_return_insn_p (void)
       && current_function_args_size >= 32768)
     return 0;
 
-  ix86_compute_frame_layout (&frame);
+  ix86_compute_frame_layout ();
+  struct ix86_frame frame = cfun->machine->frame;
   return frame.to_allocate == 0 && frame.nregs == 0;
 }
 
@@ -5757,6 +5774,234 @@ ix86_setup_frame_addresses (void)
 # define USE_HIDDEN_LINKONCE 0
 #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;
+
+/* Bit masks of integer registers, which contain branch target, used
+   by call and return thunks functions.  */
+static int indirect_thunks_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 ret_p)
+{
+  if (regno >= 0 && ret_p)
+    gcc_unreachable ();
+
+  if (USE_HIDDEN_LINKONCE)
+    {
+      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",
+		   reg_prefix, reg_names[regno]);
+	}
+      else
+      {
+	  const char *ret = ret_p ? "return" : "indirect";
+	  sprintf (name, "__x86_%s_thunk", ret);
+      }
+    }
+  else
+    {
+      if (ret_p)
+	ASM_GENERATE_INTERNAL_LABEL (name, "LRT", 0);
+      else
+	ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
+    }
+}
+
+/* Output a call and return thunk for indirect branch.
+   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 (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 */
+  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 (stack_pointer_rtx, UNITS_PER_WORD);
+      output_asm_insn ("lea\t{%a1, %0|%0, %a1}", xops);
+    }
+
+  fputs ("\tret\n", asm_out_file);
+}
+
+/* Output a funtion with a call and return thunk for indirect branch.
+   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 (int regno)
+{
+  char name[32];
+  tree decl;
+
+  /* Create __x86_indirect_thunk.  */
+  indirect_thunk_name (name, regno, false);
+  decl = build_decl (FUNCTION_DECL,
+		     get_identifier (name),
+		     build_function_type_list (void_type_node, NULL_TREE));
+  DECL_RESULT (decl) = build_decl (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)
+      {
+	DECL_COMDAT (decl) = 1;
+	make_decl_one_only (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);
+      }
+
+  if (regno < 0)
+    {
+      /* Create alias for __x86.return_thunk.  */
+      char alias[32];
+
+      indirect_thunk_name (alias, regno, true);
+#if TARGET_MACHO
+      if (TARGET_MACHO)
+	{
+	  fputs ("\t.weak_definition\t", asm_out_file);
+	  assemble_name (asm_out_file, alias);
+	  fputs ("\n\t.private_extern\t", asm_out_file);
+	  assemble_name (asm_out_file, alias);
+	  putc ('\n', asm_out_file);
+	  ASM_OUTPUT_LABEL (asm_out_file, alias);
+	}
+#else
+      ASM_OUTPUT_DEF (asm_out_file, alias, name);
+      if (USE_HIDDEN_LINKONCE)
+	{
+	  fputs ("\t.globl\t", asm_out_file);
+	  assemble_name (asm_out_file, alias);
+	  putc ('\n', asm_out_file);
+	  fputs ("\t.hidden\t", asm_out_file);
+	  assemble_name (asm_out_file, alias);
+	  putc ('\n', asm_out_file);
+	}
+#endif
+    }
+
+  DECL_INITIAL (decl) = make_node (BLOCK);
+  current_function_decl = decl;
+  allocate_struct_function (decl, false);
+  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 (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
@@ -5773,6 +6018,382 @@ get_pc_thunk_name (char name[32], unsigned int regno)
     ASM_GENERATE_INTERNAL_LABEL (name, "LPR", regno);
 }
 
+/* Remember the last target of ix86_set_current_function.  */
+static GTY(()) tree ix86_previous_fndecl;
+
+/* Set the indirect_branch_type field from VALUE string.  */
+
+static void
+ix86_set_indirect_branch_type_from_string (const char *value)
+{
+  if (value == NULL)
+    cfun->machine->indirect_branch_type = indirect_branch_keep;
+  else
+    {
+      cfun->machine->indirect_branch_type
+	= get_indirect_branch_type_from_string (value);
+      gcc_assert (cfun->machine->indirect_branch_type != indirect_branch_unset);
+    }
+}
+
+/* Set the function_return_type field from VALUE string.  */
+
+static void
+ix86_set_function_return_type_from_string (const char *value)
+{
+  if (value == NULL)
+    cfun->machine->function_return_type = indirect_branch_keep;
+  else
+    {
+      cfun->machine->function_return_type
+	= get_indirect_branch_type_from_string (value);
+      gcc_assert (cfun->machine->function_return_type != indirect_branch_unset);
+    }
+}
+
+
+/* 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);
+	  ix86_set_indirect_branch_type_from_string (TREE_STRING_POINTER (cst));
+	}
+      else
+	ix86_set_indirect_branch_type_from_string (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)
+    {
+      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);
+	  ix86_set_function_return_type_from_string (TREE_STRING_POINTER (cst));
+	}
+      else
+	ix86_set_function_return_type_from_string (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"));
+    }
+}
+
+/* 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.  */
+static void
+ix86_set_current_function (tree fndecl)
+{
+  /* Only change the context if the function changes.  This hook is called
+     several times in the course of compiling a function, and we don't want to
+     slow things down too much or call target_reinit when it isn't safe.  */
+  if (fndecl && fndecl != ix86_previous_fndecl)
+    {
+      ix86_previous_fndecl = fndecl;
+      if (cfun->machine)
+	ix86_set_indirect_branch_type (fndecl);
+    }
+}
+
+/* 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;
+  /* A sibcall via r11 register.  */
+  int regno = call_op == NULL_RTX ? R11_REG : 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);
+	  indirect_thunks_used |= 1 << i;
+	}
+      indirect_thunk_name (thunk_name_buf, regno, false);
+      thunk_name = thunk_name_buf;
+    }
+  else
+    thunk_name = NULL;
+
+  if (sibcall_p)
+    {
+      if (thunk_name != NULL)
+	fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+      else
+	output_indirect_thunk (regno);
+    }
+  else
+    {
+      if (thunk_name != NULL)
+	{
+	  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.  */
+      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)
+	fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+      else
+	output_indirect_thunk (regno);
+
+      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+      /* Call.  */
+      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.
+   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, bool sibcall_p)
+{
+  char thunk_name_buf[32];
+  char *thunk_name;
+  char push_buf[64];
+  int regno = -1;
+
+  if (cfun->machine->indirect_branch_type
+      != indirect_branch_thunk_inline)
+    {
+      if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+	indirect_thunk_needed = true;
+      indirect_thunk_name (thunk_name_buf, regno, false);
+      thunk_name = thunk_name_buf;
+    }
+  else
+    thunk_name = NULL;
+
+  snprintf (push_buf, sizeof (push_buf), "push{%c}\t%s",
+	    TARGET_64BIT ? 'q' : 'l', "%0");
+
+  if (sibcall_p)
+    {
+      output_asm_insn (push_buf, &call_op);
+      if (thunk_name != NULL)
+	fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+      else
+	output_indirect_thunk (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.  */
+      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 (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)
+	fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+      else
+	output_indirect_thunk (regno);
+
+      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+      /* Call.  */
+      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.
+   Branch is a tail call if SIBCALL_P is true.   */
+
+static void
+ix86_output_indirect_branch (rtx call_op, 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, 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, true);
+      return "";
+    }
+  else
+    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];
+
+      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, true);
+	  indirect_thunk_needed |= need_thunk;
+	  fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+	}
+      else
+	output_indirect_thunk (-1);
+
+      return "";
+    }
+
+  if (!long_p)
+    return "ret";
+
+  return "rep\n\tret";
+}
 
 /* This function generates code for -fpic that loads %ebx with
    the return address of the caller and then returns.  */
@@ -5783,10 +6404,23 @@ ix86_file_end (void)
   rtx xops[2];
   int regno;
 
+  if (indirect_thunk_needed)
+    output_indirect_thunk_function (-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 (regno);
+    }
+
   for (regno = 0; regno < 8; ++regno)
     {
       char name[32];
 
+      if ((indirect_thunks_used & (1 << regno)))
+	output_indirect_thunk_function (regno);
+
       if (! ((pic_labels_used >> regno) & 1))
 	continue;
 
@@ -6009,31 +6643,32 @@ ix86_nsaved_regs (void)
 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;
+    return frame->hard_frame_pointer_offset;
   else if (from == FRAME_POINTER_REGNUM
 	   && to == HARD_FRAME_POINTER_REGNUM)
-    return frame.hard_frame_pointer_offset - frame.frame_pointer_offset;
+    return frame->hard_frame_pointer_offset - frame->frame_pointer_offset;
   else
     {
       gcc_assert (to == STACK_POINTER_REGNUM);
 
       if (from == ARG_POINTER_REGNUM)
-	return frame.stack_pointer_offset;
+	return frame->stack_pointer_offset;
 
       gcc_assert (from == FRAME_POINTER_REGNUM);
-      return frame.stack_pointer_offset - frame.frame_pointer_offset;
+      return frame->stack_pointer_offset - frame->frame_pointer_offset;
     }
 }
 
 /* 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;
   HOST_WIDE_INT total_size;
   unsigned int stack_alignment_needed;
   HOST_WIDE_INT offset;
@@ -6325,10 +6960,10 @@ ix86_expand_prologue (void)
 {
   rtx insn;
   bool pic_reg_used;
-  struct ix86_frame frame;
   HOST_WIDE_INT allocate;
 
-  ix86_compute_frame_layout (&frame);
+  ix86_compute_frame_layout ();
+  struct ix86_frame frame = cfun->machine->frame;
 
   if (cfun->machine->force_align_arg_pointer)
     {
@@ -6565,7 +7200,8 @@ ix86_expand_epilogue (int style)
   struct ix86_frame frame;
   HOST_WIDE_INT offset;
 
-  ix86_compute_frame_layout (&frame);
+  ix86_compute_frame_layout ();
+  frame = cfun->machine->frame;
 
   /* See the comment about red zone and frame
      pointer usage in ix86_expand_prologue.  */
@@ -8663,7 +9299,8 @@ put_condition_code (enum rtx_code code, enum machine_mode mode, int reverse,
    If CODE is 'k', pretend the mode is SImode.
    If CODE is 'q', pretend the mode is DImode.
    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 'y', print "st(0)" instead of "st", if the reg is stack op.
+   If CODE is 'V', print naked full integer register name without %.  */
 
 void
 print_reg (rtx x, int code, FILE *file)
@@ -8675,7 +9312,7 @@ print_reg (rtx x, int code, FILE *file)
 		  && REGNO (x) != FPSR_REG
 		  && REGNO (x) != FPCR_REG));
 
-  if (ASSEMBLER_DIALECT == ASM_ATT)
+  if (ASSEMBLER_DIALECT == ASM_ATT && code != 'V')
     putc ('%', file);
 
   if (x == pc_rtx)
@@ -8700,6 +9337,14 @@ print_reg (rtx x, int code, FILE *file)
   else
     code = GET_MODE_SIZE (GET_MODE (x));
 
+  if (code == 'V')
+    {
+      if (REGNO (x))
+	code = GET_MODE_SIZE (word_mode);
+      else
+	error ("'V' modifier on non-integer register");
+    }
+
   /* Irritatingly, AMD extended registers use different naming convention
      from the normal registers.  */
   if (REX_INT_REG_P (x))
@@ -8825,6 +9470,7 @@ get_some_local_dynamic_name (void)
    & -- 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 SSE5 com* 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).
  */
@@ -8972,6 +9618,7 @@ print_operand (FILE *file, rtx x, int code)
 	case 'y':
 	case 'X':
 	case 'P':
+	case 'V':
 	  break;
 
 	case 's':
@@ -16346,6 +16993,56 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
     CALL_INSN_FUNCTION_USAGE (call) = use;
 }
 
+/* Output the assembly for a call instruction.  */
+
+const char *
+ix86_output_call_insn (rtx insn, rtx call_op, int addr_op)
+{
+  bool r11_sibcall_p = call_op == NULL_RTX;
+  bool direct_p = !r11_sibcall_p && constant_call_address_operand (call_op, Pmode);
+  bool output_indirect_p
+    = cfun->machine->indirect_branch_type != indirect_branch_keep;
+
+  gcc_assert (addr_op == 0 || addr_op == 1);
+
+  if (SIBLING_CALL_P (insn))
+    {
+      if (direct_p)
+	return addr_op ? "jmp\t%P1" : "jmp\t%P0";
+      else
+	{
+	  if (output_indirect_p)
+	    {
+	      if (r11_sibcall_p)
+		ix86_output_indirect_branch_via_reg (NULL_RTX, true);
+	      else
+		ix86_output_indirect_branch (call_op, true);
+	      return "";
+	    }
+	  else
+	    {
+	      if (r11_sibcall_p)
+		return "jmp\t{*%%}r11";
+	      else
+		return addr_op ? "jmp\t%A1" : "jmp\t%A0";
+	    }
+	}
+    }
+
+  if (direct_p)
+    return addr_op ? "call\t%P1" : "call\t%P0";
+  else
+    {
+      if (output_indirect_p)
+	{
+	  ix86_output_indirect_branch (call_op, false);
+	  return "";
+	}
+      else
+	return addr_op ? "call\t%A1" : "call\t%A0";
+    }
+}
+
 
 /* Clear stack slot assignments remembered from previous functions.
    This is called from INIT_EXPANDERS once before RTL is emitted for each
@@ -22801,6 +23498,65 @@ ix86_handle_struct_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_fndecl_attribute (tree *node, tree name,
+                              tree args,
+                              int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+               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;
+	}
+    }
+
+  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;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -25345,6 +26101,8 @@ static const struct attribute_spec ix86_attribute_table[] =
 #endif
   { "ms_struct", 0, 0, false, false,  false, ix86_handle_struct_attribute },
   { "gcc_struct", 0, 0, false, false,  false, ix86_handle_struct_attribute },
+  { "indirect_branch", 1, 1, true, false, false, ix86_handle_fndecl_attribute },
+  { "function_return", 1, 1, true, false, false, ix86_handle_fndecl_attribute },
 #ifdef SUBTARGET_ATTRIBUTE_TABLE
   SUBTARGET_ATTRIBUTE_TABLE,
 #endif
@@ -25557,6 +26315,9 @@ x86_builtin_vectorization_cost (bool runtime_test)
 #undef TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST
 #define TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST x86_builtin_vectorization_cost
 
+#undef TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION ix86_set_current_function
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 0711b01ca2f..2caf240fd91 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1202,6 +1202,9 @@ do {									\
 /* First floating point reg */
 #define FIRST_FLOAT_REG 8
 
+#define FIRST_INT_REG AX_REG
+#define LAST_INT_REG  SP_REG
+
 /* First & last stack-like regs */
 #define FIRST_STACK_REG FIRST_FLOAT_REG
 #define LAST_STACK_REG (FIRST_FLOAT_REG + 7)
@@ -1224,6 +1227,9 @@ do {									\
    This is computed in `reload', in reload1.c.  */
 #define FRAME_POINTER_REQUIRED  ix86_frame_pointer_required ()
 
+#define LEGACY_INT_REGNO_P(N) (IN_RANGE ((N), FIRST_INT_REG, LAST_INT_REG))
+#define LEGACY_INT_REG_P(X) (REG_P (X) && LEGACY_INT_REGNO_P (REGNO (X)))
+
 /* Override this in other tm.h files to cope with various OS lossage
    requiring a frame pointer.  */
 #ifndef SUBTARGET_FRAME_POINTER_REQUIRED
@@ -2390,6 +2396,19 @@ enum ix86_stack_slot
   MAX_386_STACK_LOCALS
 };
 
+/* 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
+};
+
 /* Define this macro if the port needs extra instructions inserted
    for mode switching in an optimizing compilation.  */
 
@@ -2442,6 +2461,47 @@ enum ix86_stack_slot
 
 #define FASTCALL_PREFIX '@'
 
+#ifndef USED_FOR_TARGET
+/* Structure describing stack frame layout.
+   Stack grows downward:
+
+   [arguments]
+					      <- ARG_POINTER
+   saved pc
+
+   saved frame pointer if frame_pointer_needed
+					      <- HARD_FRAME_POINTER
+   [saved regs]
+
+   [padding1]          \
+		        )
+   [va_arg registers]  (
+		        > to_allocate	      <- FRAME_POINTER
+   [frame]	       (
+		        )
+   [padding2]	       /
+  */
+struct ix86_frame GTY(())
+{
+  int nregs;
+  int padding1;
+  int va_arg_size;
+  HOST_WIDE_INT frame;
+  int padding2;
+  int outgoing_arguments_size;
+  int red_zone_size;
+
+  HOST_WIDE_INT to_allocate;
+  /* 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;
+
+  /* When save_regs_using_mov is set, emit prologue using
+     move instead of push instructions.  */
+  bool save_regs_using_mov;
+};
+
 struct machine_function GTY(())
 {
   struct stack_local_entry *stack_locals;
@@ -2451,6 +2511,10 @@ struct machine_function GTY(())
   int varargs_fpr_size;
   int accesses_prev_frame;
   int optimize_mode_switching[MAX_386_ENTITIES];
+
+  /* Cached initial frame layout for the current function.  */
+  struct ix86_frame frame;
+
   int needs_cld;
   /* Set by ix86_compute_frame_layout and used by prologue/epilogue
      expander to determine the style used.  */
@@ -2468,7 +2532,18 @@ struct machine_function GTY(())
      ix86_current_function_calls_tls_descriptor macro for a better
      approximation.  */
   int tls_descriptor_call_expanded_p;
+
+  /* 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;
+
+  /* How to generate function return.  */
+  ENUM_BITFIELD(indirect_branch) function_return_type : 3;
 };
+#endif
 
 #define ix86_stack_locals (cfun->machine->stack_locals)
 #define ix86_varargs_gpr_size (cfun->machine->varargs_gpr_size)
@@ -2484,6 +2559,7 @@ struct machine_function GTY(())
    REG_SP is live.  */
 #define ix86_current_function_calls_tls_descriptor \
   (ix86_tls_descriptor_calls_expanded_in_cfun && df_regs_ever_live_p (SP_REG))
+#define ix86_red_zone_size (cfun->machine->frame.red_zone_size)
 
 /* Control behavior of x86_file_start.  */
 #define X86_FILE_START_VERSION_DIRECTIVE false
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index f292383941a..53dd72ca296 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -14546,13 +14546,23 @@
 (define_expand "indirect_jump"
   [(set (pc) (match_operand 0 "nonimmediate_operand" ""))]
   ""
-  "")
+{
+  if (ix86_indirect_branch_register)
+     operands[0] = convert_memory_address (word_mode, operands[0]);
+
+  cfun->machine->has_local_indirect_jump = true;
+})
 
 (define_insn "*indirect_jump"
   [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))]
   "!TARGET_64BIT"
-  "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 "type" "ibr")
    (set_attr "length_immediate" "0")])
 
 (define_insn "*indirect_jump_rtx64"
@@ -14598,21 +14608,48 @@
       operands[0] = expand_simple_binop (Pmode, code, op0, op1, NULL_RTX, 0,
 					 OPTAB_DIRECT);
     }
+
+  if (ix86_indirect_branch_register)
+    cfun->machine->has_local_indirect_jump = true;
 })
 
 (define_insn "*tablejump_1"
   [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))
    (use (label_ref (match_operand 1 "" "")))]
+  "!TARGET_64BIT && !ix86_indirect_branch_register"
+  "* return ix86_output_indirect_jmp (operands[0], false);"
+  [(set (attr "type")
+     (if_then_else (match_test "(cfun->machine->indirect_branch_type
+				 != indirect_branch_keep)")
+	(const_string "multi")
+	(const_string "ibr")))
+   (set_attr "length_immediate" "0")])
+
+(define_insn "*tablejump_1_reg"
+  [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "r"))
+   (use (label_ref (match_operand 1 "" "")))]
   "!TARGET_64BIT"
-  "jmp\t%A0"
-  [(set_attr "type" "ibr")
+  "* return ix86_output_indirect_jmp (operands[0], false);"
+  [(set (attr "type")
+     (if_then_else (match_test "(cfun->machine->indirect_branch_type
+				 != indirect_branch_keep)")
+	(const_string "multi")
+	(const_string "ibr")))
    (set_attr "length_immediate" "0")])
 
 (define_insn "*tablejump_1_rtx64"
   [(set (pc) (match_operand:DI 0 "nonimmediate_operand" "rm"))
    (use (label_ref (match_operand 1 "" "")))]
+  "TARGET_64BIT && !ix86_indirect_branch_register"
+  "* return ix86_output_indirect_jmp (operands[0], false);"
+  [(set_attr "type" "ibr")
+   (set_attr "length_immediate" "0")])
+
+(define_insn "*tablejump_1_rtx64_reg"
+  [(set (pc) (match_operand:DI 0 "nonimmediate_operand" "r"))
+   (use (label_ref (match_operand 1 "" "")))]
   "TARGET_64BIT"
-  "jmp\t%A0"
+  "* return ix86_output_indirect_jmp (operands[0], false);"
   [(set_attr "type" "ibr")
    (set_attr "length_immediate" "0")])
 
@@ -14685,12 +14722,7 @@
    (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
 			    (match_operand:SI 2 "immediate_operand" "")))]
   "!TARGET_64BIT"
-{
-  if (SIBLING_CALL_P (insn))
-    return "jmp\t%P0";
-  else
-    return "call\t%P0";
-}
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_insn "*call_pop_1"
@@ -14698,12 +14730,17 @@
 	 (match_operand:SI 1 "" ""))
    (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
 			    (match_operand:SI 2 "immediate_operand" "i")))]
-  "!SIBLING_CALL_P (insn) && !TARGET_64BIT"
-{
-  if (constant_call_address_operand (operands[0], Pmode))
-    return "call\t%P0";
-  return "call\t%A0";
-}
+  "!SIBLING_CALL_P (insn) && !TARGET_64BIT && ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[0], 0); }
+  [(set_attr "type" "call")])
+
+(define_insn "*call_pop_1_reg"
+  [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "ls"))
+	 (match_operand:SI 1 "" ""))
+   (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
+			    (match_operand:SI 2 "immediate_operand" "i")))]
+  "!TARGET_64BIT && !SIBLING_CALL_P (insn)"
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_insn "*sibcall_pop_1"
@@ -14712,11 +14749,7 @@
    (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
 			    (match_operand:SI 2 "immediate_operand" "i,i,i,i")))]
   "SIBLING_CALL_P (insn) && !TARGET_64BIT"
-{
-  if (constant_call_address_operand (operands[0], Pmode))
-    return "jmp\t%P0";
-  return "jmp\t%A0";
-}
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_expand "call"
@@ -14743,67 +14776,73 @@
   [(call (mem:QI (match_operand 0 "constant_call_address_operand" ""))
 	 (match_operand 1 "" ""))]
   ""
-{
-  if (SIBLING_CALL_P (insn))
-    return "jmp\t%P0";
-  else
-    return "call\t%P0";
-}
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_insn "*call_1"
   [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "rsm"))
 	 (match_operand 1 "" ""))]
-  "!SIBLING_CALL_P (insn) && !TARGET_64BIT"
-{
-  if (constant_call_address_operand (operands[0], Pmode))
-    return "call\t%P0";
-  return "call\t%A0";
-}
+  "!TARGET_64BIT && !SIBLING_CALL_P (insn) && !ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[0], 0); }
+  [(set_attr "type" "call")])
+
+(define_insn "*call_1_reg"
+  [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "ls"))
+	 (match_operand 1 "" ""))]
+  "!TARGET_64BIT && !SIBLING_CALL_P (insn)"
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_insn "*sibcall_1"
   [(call (mem:QI (match_operand:SI 0 "sibcall_insn_operand" "s,c,d,a"))
 	 (match_operand 1 "" ""))]
   "SIBLING_CALL_P (insn) && !TARGET_64BIT"
-{
-  if (constant_call_address_operand (operands[0], Pmode))
-    return "jmp\t%P0";
-  return "jmp\t%A0";
-}
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_insn "*call_1_rex64"
   [(call (mem:QI (match_operand:DI 0 "call_insn_operand" "rsm"))
 	 (match_operand 1 "" ""))]
-  "!SIBLING_CALL_P (insn) && TARGET_64BIT
+  "TARGET_64BIT && !SIBLING_CALL_P (insn)
+   && ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC
+   && !ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[0], 0); }
+  [(set_attr "type" "call")])
+
+(define_insn "*call_1_rex64_reg"
+  [(call (mem:QI (match_operand:DI 0 "call_insn_operand" "rs"))
+	 (match_operand 1 "" ""))]
+  "TARGET_64BIT && !SIBLING_CALL_P (insn)
    && ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC"
-{
-  if (constant_call_address_operand (operands[0], Pmode))
-    return "call\t%P0";
-  return "call\t%A0";
-}
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_insn "*call_1_rex64_large"
   [(call (mem:QI (match_operand:DI 0 "call_insn_operand" "rm"))
 	 (match_operand 1 "" ""))]
-  "!SIBLING_CALL_P (insn) && TARGET_64BIT"
-  "call\t%A0"
+  "TARGET_64BIT && !SIBLING_CALL_P (insn) && !ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[0], 0); }
+  [(set_attr "type" "call")])
+
+(define_insn "*call_1_rex64_large_reg"
+  [(call (mem:QI (match_operand:DI 0 "call_insn_operand" "r"))
+	 (match_operand 1 "" ""))]
+  "TARGET_64BIT && !SIBLING_CALL_P (insn)"
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_insn "*sibcall_1_rex64"
   [(call (mem:QI (match_operand:DI 0 "constant_call_address_operand" ""))
 	 (match_operand 1 "" ""))]
   "SIBLING_CALL_P (insn) && TARGET_64BIT"
-  "jmp\t%P0"
+  { return ix86_output_call_insn (insn, operands[0], 0); }
   [(set_attr "type" "call")])
 
 (define_insn "*sibcall_1_rex64_v"
   [(call (mem:QI (reg:DI R11_REG))
 	 (match_operand 0 "" ""))]
   "SIBLING_CALL_P (insn) && TARGET_64BIT"
-  "jmp\t{*%%}r11"
+  { return ix86_output_call_insn (insn, NULL_RTX, 0); }
   [(set_attr "type" "call")])
 
 
@@ -14939,7 +14978,7 @@
 (define_insn "return_internal"
   [(return)]
   "reload_completed"
-  "ret"
+  "* return ix86_output_function_return (false);"
   [(set_attr "length" "1")
    (set_attr "length_immediate" "0")
    (set_attr "modrm" "0")])
@@ -14951,7 +14990,7 @@
   [(return)
    (unspec [(const_int 0)] UNSPEC_REP)]
   "reload_completed"
-  "rep\;ret"
+  "* return ix86_output_function_return (true);"
   [(set_attr "length" "1")
    (set_attr "length_immediate" "0")
    (set_attr "prefix_rep" "1")
@@ -14970,8 +15009,12 @@
   [(return)
    (use (match_operand:SI 0 "register_operand" "r"))]
   "reload_completed"
-  "jmp\t%A0"
-  [(set_attr "type" "ibr")
+  "* return ix86_output_indirect_jmp (operands[0], true);"
+  [(set (attr "type")
+     (if_then_else (match_test "(cfun->machine->indirect_branch_type
+				 != indirect_branch_keep)")
+	(const_string "multi")
+	(const_string "ibr")))
    (set_attr "length_immediate" "0")])
 
 (define_insn "nop"
@@ -21066,12 +21109,7 @@
    (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
 			    (match_operand:SI 3 "immediate_operand" "")))]
   "!TARGET_64BIT"
-{
-  if (SIBLING_CALL_P (insn))
-    return "jmp\t%P1";
-  else
-    return "call\t%P1";
-}
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*call_value_pop_1"
@@ -21080,12 +21118,18 @@
 	      (match_operand:SI 2 "" "")))
    (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
 			    (match_operand:SI 3 "immediate_operand" "i")))]
-  "!SIBLING_CALL_P (insn) && !TARGET_64BIT"
-{
-  if (constant_call_address_operand (operands[1], Pmode))
-    return "call\t%P1";
-  return "call\t%A1";
-}
+  "!SIBLING_CALL_P (insn) && !TARGET_64BIT && !ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
+  [(set_attr "type" "callv")])
+
+(define_insn "*call_value_pop_1_reg"
+  [(set (match_operand 0 "" "")
+	(call (mem:QI (match_operand:SI 1 "call_insn_operand" "ls"))
+	      (match_operand:SI 2 "" "")))
+   (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
+			    (match_operand:SI 3 "immediate_operand" "i")))]
+  "!TARGET_64BIT && !SIBLING_CALL_P (insn)"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*sibcall_value_pop_1"
@@ -21095,11 +21139,7 @@
    (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
 			    (match_operand:SI 3 "immediate_operand" "i,i,i,i")))]
   "SIBLING_CALL_P (insn) && !TARGET_64BIT"
-{
-  if (constant_call_address_operand (operands[1], Pmode))
-    return "jmp\t%P1";
-  return "jmp\t%A1";
-}
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*call_value_0"
@@ -21107,12 +21147,7 @@
 	(call (mem:QI (match_operand:SI 1 "constant_call_address_operand" ""))
 	      (match_operand:SI 2 "" "")))]
   "!TARGET_64BIT"
-{
-  if (SIBLING_CALL_P (insn))
-    return "jmp\t%P1";
-  else
-    return "call\t%P1";
-}
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*call_value_0_rex64"
@@ -21120,24 +21155,39 @@
 	(call (mem:QI (match_operand:DI 1 "constant_call_address_operand" ""))
 	      (match_operand:DI 2 "const_int_operand" "")))]
   "TARGET_64BIT"
-{
-  if (SIBLING_CALL_P (insn))
-    return "jmp\t%P1";
-  else
-    return "call\t%P1";
-}
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*call_value_1"
   [(set (match_operand 0 "" "")
 	(call (mem:QI (match_operand:SI 1 "call_insn_operand" "rsm"))
 	      (match_operand:SI 2 "" "")))]
-  "!SIBLING_CALL_P (insn) && !TARGET_64BIT"
-{
-  if (constant_call_address_operand (operands[1], Pmode))
-    return "call\t%P1";
-  return "call\t%A1";
-}
+  "!SIBLING_CALL_P (insn) && !TARGET_64BIT && !ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
+  [(set_attr "type" "callv")])
+
+(define_insn "*call_value_1_reg"
+  [(set (match_operand 0 "" "")
+	(call (mem:QI (match_operand:SI 1 "call_insn_operand" "ls"))
+	      (match_operand:SI 2 "" "")))]
+  "!TARGET_64BIT && !SIBLING_CALL_P (insn)"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
+  [(set_attr "type" "callv")])
+
+(define_insn "*call_value_1"
+  [(set (match_operand 0 "" "")
+	(call (mem:QI (match_operand:SI 1 "call_insn_operand" "lsm"))
+	      (match_operand:SI 2 "" "")))]
+  "!TARGET_64BIT && !SIBLING_CALL_P (insn) && !ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
+  [(set_attr "type" "callv")])
+
+(define_insn "*call_value_1_reg"
+  [(set (match_operand 0 "" "")
+	(call (mem:QI (match_operand:SI 1 "call_insn_operand" "ls"))
+	      (match_operand:SI 2 "" "")))]
+  "!TARGET_64BIT && !SIBLING_CALL_P (insn)"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*sibcall_value_1"
@@ -21145,32 +21195,42 @@
 	(call (mem:QI (match_operand:SI 1 "sibcall_insn_operand" "s,c,d,a"))
 	      (match_operand:SI 2 "" "")))]
   "SIBLING_CALL_P (insn) && !TARGET_64BIT"
-{
-  if (constant_call_address_operand (operands[1], Pmode))
-    return "jmp\t%P1";
-  return "jmp\t%A1";
-}
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*call_value_1_rex64"
   [(set (match_operand 0 "" "")
 	(call (mem:QI (match_operand:DI 1 "call_insn_operand" "rsm"))
 	      (match_operand:DI 2 "" "")))]
-  "!SIBLING_CALL_P (insn) && TARGET_64BIT
+  "!SIBLING_CALL_P (insn) && TARGET_64BIT 
+   && ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC
+   && !ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
+  [(set_attr "type" "callv")])
+
+(define_insn "*call_value_1_rex64_reg"
+  [(set (match_operand 0 "" "")
+	(call (mem:QI (match_operand:DI 1 "call_insn_operand" "rs"))
+	      (match_operand:DI 2 "" "")))]
+  "TARGET_64BIT && !SIBLING_CALL_P (insn)
    && ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC"
-{
-  if (constant_call_address_operand (operands[1], Pmode))
-    return "call\t%P1";
-  return "call\t%A1";
-}
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*call_value_1_rex64_large"
   [(set (match_operand 0 "" "")
 	(call (mem:QI (match_operand:DI 1 "call_insn_operand" "rm"))
 	      (match_operand:DI 2 "" "")))]
-  "!SIBLING_CALL_P (insn) && TARGET_64BIT"
-  "call\t%A1"
+  "!SIBLING_CALL_P (insn) && TARGET_64BIT && !ix86_indirect_branch_register"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
+  [(set_attr "type" "callv")])
+
+(define_insn "*call_value_1_rex64_large_reg"
+  [(set (match_operand 0 "" "")
+	(call (mem:QI (match_operand:DI 1 "call_insn_operand" "r"))
+	      (match_operand:DI 2 "" "")))]
+  "TARGET_64BIT && !SIBLING_CALL_P (insn)"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*sibcall_value_1_rex64"
@@ -21178,7 +21238,7 @@
 	(call (mem:QI (match_operand:DI 1 "constant_call_address_operand" ""))
 	      (match_operand:DI 2 "" "")))]
   "SIBLING_CALL_P (insn) && TARGET_64BIT"
-  "jmp\t%P1"
+  { return ix86_output_call_insn (insn, operands[1], 1); }
   [(set_attr "type" "callv")])
 
 (define_insn "*sibcall_value_1_rex64_v"
@@ -21186,7 +21246,7 @@
 	(call (mem:QI (reg:DI R11_REG))
 	      (match_operand:DI 1 "" "")))]
   "SIBLING_CALL_P (insn) && TARGET_64BIT"
-  "jmp\t{*%%}r11"
+  { return ix86_output_call_insn (insn, NULL_RTX, 1); }
   [(set_attr "type" "callv")])
 
 ;; We used to use "int $5", in honor of #BR which maps to interrupt vector 5.
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 75c94ba771e..467f83a8c14 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -287,3 +287,31 @@ Support AES built-in functions and code generation
 mpclmul
 Target Report RejectNegative Var(x86_pclmul)
 Support PCLMUL built-in functions and code generation
+
+mindirect-branch=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(0)
+Convert indirect call and jump to call and return thunks.
+
+mfunction-return=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_function_return) Init(0)
+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=/-mfunction-return= options):
+
+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)
+
+mindirect-branch-register
+Target Report Var(ix86_indirect_branch_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 7ba72899224..0d81b065f29 100644
--- a/gcc/config/i386/predicates.md
+++ b/gcc/config/i386/predicates.md
@@ -546,7 +546,8 @@
 (define_predicate "call_insn_operand"
   (ior (match_operand 0 "constant_call_address_operand")
        (ior (match_operand 0 "register_no_elim_operand")
-	    (match_operand 0 "memory_operand"))))
+	    (and (not (match_test "ix86_indirect_branch_register"))
+		 (match_operand 0 "memory_operand")))))
 
 ;; Similarly, but for tail calls, in which we cannot allow memory references.
 (define_predicate "sibcall_insn_operand"
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 853ab40f5e2..00f6f274693 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -560,7 +560,8 @@ Objective-C and Objective-C++ Dialects}.
 -momit-leaf-frame-pointer  -mno-red-zone -mno-tls-direct-seg-refs @gol
 -mcmodel=@var{code-model} @gol
 -m32  -m64 -mlarge-data-threshold=@var{num} @gol
--mfused-madd -mno-fused-madd}
+-mindirect-branch=@var{choice} -mfunction-return==@var{choice} @gol
+-mindirect-branch-register}
 
 @emph{IA-64 Options}
 @gccoptlist{-mbig-endian  -mlittle-endian  -mgnu-as  -mgnu-ld  -mno-pic @gol
@@ -15290,6 +15291,42 @@ the DSOs.
 An overview of these techniques, their benefits and how to use them
 is at @w{@uref{http://gcc.gnu.org/wiki/Visibility}}.
 
+@item -mindirect-branch=@var{choice}
+@opindex -mindirect-branch
+Convert indirect call and jump with @var{choice}.  The default is
+@samp{keep}, which keeps indirect call and jump unmodified.
+@samp{thunk} converts indirect call and jump to call and return thunk.
+@samp{thunk-inline} converts indirect call and jump to inlined call
+and return thunk.  @samp{thunk-extern} converts indirect call and jump
+to external call and return thunk provided in a separate object file.
+You can control this behavior for a specific function by using the
+function attribute @code{indirect_branch}.  @xref{Function Attributes}.
+
+Note that @option{-mcmodel=large} is incompatible with
+@option{-mindirect-branch=thunk} nor
+@option{-mindirect-branch=thunk-extern} since the thunk function may
+not be reachable in large code model.
+
+@item -mfunction-return=@var{choice}
+@opindex -mfunction-return
+Convert function return with @var{choice}.  The default is @samp{keep},
+which keeps function return unmodified.  @samp{thunk} converts function
+return to call and return thunk.  @samp{thunk-inline} converts function
+return to inlined call and return thunk.  @samp{thunk-extern} converts
+function return to external call and return thunk provided in a separate
+object file.  You can control this behavior for a specific function by
+using the function attribute @code{function_return}.
+@xref{Function Attributes}.
+
+Note that @option{-mcmodel=large} is incompatible with
+@option{-mfunction-return=thunk} nor
+@option{-mfunction-return=thunk-extern} since the thunk function may
+not be reachable in large code model.
+
+@item -mindirect-branch-register
+@opindex -mindirect-branch-register
+Force indirect call and jump via register.
+
 @end table
 
 @c man end
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index fb7520b71e8..676bbf493ca 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -6603,6 +6603,30 @@ string).
 
 The @var{constraints} operand is ignored and should be the null string.
 
+@cindex @code{match_test} and attributes
+@item (match_test @var{c-expr})
+The test is true if C expression @var{c-expr} is true.  In non-constant
+attributes, @var{c-expr} has access to the following variables:
+
+@table @var
+@item insn
+The rtl instruction under test.
+@item which_alternative
+The @code{define_insn} alternative that @var{insn} matches.
+@xref{Output Statement}.
+@item operands
+An array of @var{insn}'s rtl operands.
+@end table
+
+@var{c-expr} behaves like the condition in a C @code{if} statement,
+so there is no need to explicitly convert the expression into a boolean
+0 or 1 value.  For example, the following two tests are equivalent:
+
+@smallexample
+(match_test "x & 2")
+(match_test "(x & 2) != 0")
+@end smallexample
+
 @cindex @code{le} and attributes
 @cindex @code{leu} and attributes
 @cindex @code{lt} and attributes
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 385779afc2a..b21ed2eabdd 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -645,6 +645,7 @@ attr_copy_rtx (rtx orig)
     case CONST_DOUBLE:
     case CONST_VECTOR:
     case SYMBOL_REF:
+    case MATCH_TEST:
     case CODE_LABEL:
     case PC:
     case CC0:
@@ -828,6 +829,11 @@ check_attr_test (rtx exp, int is_const, int lineno)
       XEXP (exp, 0) = check_attr_test (XEXP (exp, 0), is_const, lineno);
       break;
 
+    case MATCH_TEST:
+      exp = attr_rtx (MATCH_TEST, XSTR (exp, 0));
+      ATTR_IND_SIMPLIFIED_P (exp) = 1;
+      break;
+
     case MATCH_OPERAND:
       if (is_const)
 	fatal ("RTL operator \"%s\" not valid in constant attribute test",
@@ -2886,6 +2892,7 @@ clear_struct_flag (rtx x)
     case CONST_INT:
     case CONST_DOUBLE:
     case CONST_VECTOR:
+    case MATCH_TEST:
     case SYMBOL_REF:
     case CODE_LABEL:
     case PC:
@@ -3390,6 +3397,11 @@ write_test_expr (rtx exp, int flags)
       printf (HOST_WIDE_INT_PRINT_DEC, XWINT (exp, 0));
       break;
 
+    case MATCH_TEST:
+      print_c_condition (XSTR (exp, 0));
+      printf (" != 0");
+      break;
+
     /* A random C expression.  */
     case SYMBOL_REF:
       print_c_condition (XSTR (exp, 0));
@@ -3583,6 +3595,7 @@ walk_attr_value (rtx exp)
       must_extract = 1;
       return;
 
+    case MATCH_TEST:
     case EQ_ATTR_ALT:
       must_extract = must_constrain = 1;
       break;
diff --git a/gcc/rtl.def b/gcc/rtl.def
index fa2238c58b7..e537ca4a5ba 100644
--- a/gcc/rtl.def
+++ b/gcc/rtl.def
@@ -811,9 +811,8 @@ DEF_RTL_EXPR(MATCH_PAR_DUP, "match_par_dup", "iE", RTX_MATCH)
    the result of the one before it.  */
 DEF_RTL_EXPR(MATCH_CODE, "match_code", "ss", RTX_MATCH)
 
-/* Appears only in define_predicate/define_special_predicate
-    expressions.  The argument is a C expression to be injected at this
-    point in the predicate formula.  */
+/* Used to inject a C conditional expression into an .md file.  It can
+   appear in a predicate definition or an attribute expression.  */
 DEF_RTL_EXPR(MATCH_TEST, "match_test", "s", RTX_MATCH)
 
 /* Insn (and related) definitions.  */
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..7835c744f4c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+
+__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..ab81082c4ad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+
+__attribute__ ((indirect_branch("thunk-inline")))
+void
+bar (void)
+{
+}
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..4beb0c64a41
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -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 { *-*-linux* } } } } */
+/* { 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" } } */
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..49ff71bc258
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -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 { *-*-linux* } } } } */
+/* { 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" } } */
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..d730d31bda1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -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-attr-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
new file mode 100644
index 00000000000..676428de407
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+
+__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/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
new file mode 100644
index 00000000000..51363d1167f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -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 { *-*-linux* } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
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..4d01582fc54
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -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 { *-*-linux* } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
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..e83b81666cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -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 { *-*-linux* } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
+/* { 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/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-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-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..5c43c760a91
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=keep -mcmodel=large" } */
+
+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-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-20.c b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
new file mode 100644
index 00000000000..eea8082ee3c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-20.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-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..6bc7d5a88e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-21.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-inline")))
+void
+bar (void)
+{
+}
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" } } */
