]> git.pld-linux.org Git - packages/gcc.git/blob - gcc-retpoline.patch
- rel 6; fixes branch diff patch; fix in branch.sh
[packages/gcc.git] / gcc-retpoline.patch
1 commit 9005adea32ef0cc14b3ef7ceacf5b67bf0862194
2 Author: H.J. Lu <hjl.tools@gmail.com>
3 Date:   Mon Nov 6 09:11:08 2017 -0800
4
5     i386: Move struct ix86_frame to machine_function
6     
7     Make ix86_frame available to i386 code generation.
8     
9             * config/i386/i386.c (ix86_frame): Moved to ...
10             * config/i386/i386.h (ix86_frame): Here.
11             (machine_function): Add frame.
12             * config/i386/i386.c (ix86_compute_frame_layout): Repace the
13             frame argument with &cfun->machine->frame.
14             (ix86_can_use_return_insn_p): Don't pass &frame to
15             ix86_compute_frame_layout.  Copy frame from cfun->machine->frame.
16             (ix86_can_eliminate): Likewise.
17             (ix86_expand_prologue): Likewise.
18             (ix86_expand_epilogue): Likewise.
19             (ix86_expand_split_stack_prologue): Likewise.
20
21 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
22 index 8a3782c0298..813337242d8 100644
23 --- a/gcc/config/i386/i386.c
24 +++ b/gcc/config/i386/i386.c
25 @@ -2444,53 +2444,6 @@ struct GTY(()) stack_local_entry {
26    struct stack_local_entry *next;
27  };
28  
29 -/* Structure describing stack frame layout.
30 -   Stack grows downward:
31 -
32 -   [arguments]
33 -                                       <- ARG_POINTER
34 -   saved pc
35 -
36 -   saved static chain                  if ix86_static_chain_on_stack
37 -
38 -   saved frame pointer                 if frame_pointer_needed
39 -                                       <- HARD_FRAME_POINTER
40 -   [saved regs]
41 -                                       <- regs_save_offset
42 -   [padding0]
43 -
44 -   [saved SSE regs]
45 -                                       <- sse_regs_save_offset
46 -   [padding1]          |
47 -                      |                <- FRAME_POINTER
48 -   [va_arg registers]  |
49 -                      |
50 -   [frame]            |
51 -                      |
52 -   [padding2]         | = to_allocate
53 -                                       <- STACK_POINTER
54 -  */
55 -struct ix86_frame
56 -{
57 -  int nsseregs;
58 -  int nregs;
59 -  int va_arg_size;
60 -  int red_zone_size;
61 -  int outgoing_arguments_size;
62 -
63 -  /* The offsets relative to ARG_POINTER.  */
64 -  HOST_WIDE_INT frame_pointer_offset;
65 -  HOST_WIDE_INT hard_frame_pointer_offset;
66 -  HOST_WIDE_INT stack_pointer_offset;
67 -  HOST_WIDE_INT hfp_save_offset;
68 -  HOST_WIDE_INT reg_save_offset;
69 -  HOST_WIDE_INT sse_reg_save_offset;
70 -
71 -  /* When save_regs_using_mov is set, emit prologue using
72 -     move instead of push instructions.  */
73 -  bool save_regs_using_mov;
74 -};
75 -
76  /* Which cpu are we scheduling for.  */
77  enum attr_cpu ix86_schedule;
78  
79 @@ -2582,7 +2535,7 @@ static unsigned int ix86_function_arg_boundary (machine_mode,
80                                                 const_tree);
81  static rtx ix86_static_chain (const_tree, bool);
82  static int ix86_function_regparm (const_tree, const_tree);
83 -static void ix86_compute_frame_layout (struct ix86_frame *);
84 +static void ix86_compute_frame_layout (void);
85  static bool ix86_expand_vector_init_one_nonzero (bool, machine_mode,
86                                                  rtx, rtx, int);
87  static void ix86_add_new_builtins (HOST_WIDE_INT, HOST_WIDE_INT);
88 @@ -11903,7 +11856,8 @@ ix86_can_use_return_insn_p (void)
89    if (crtl->args.pops_args && crtl->args.size >= 32768)
90      return 0;
91  
92 -  ix86_compute_frame_layout (&frame);
93 +  ix86_compute_frame_layout ();
94 +  frame = cfun->machine->frame;
95    return (frame.stack_pointer_offset == UNITS_PER_WORD
96           && (frame.nregs + frame.nsseregs) == 0);
97  }
98 @@ -12389,8 +12343,8 @@ ix86_can_eliminate (const int from, const int to)
99  HOST_WIDE_INT
100  ix86_initial_elimination_offset (int from, int to)
101  {
102 -  struct ix86_frame frame;
103 -  ix86_compute_frame_layout (&frame);
104 +  ix86_compute_frame_layout ();
105 +  struct ix86_frame frame = cfun->machine->frame;
106  
107    if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
108      return frame.hard_frame_pointer_offset;
109 @@ -12429,8 +12383,9 @@ ix86_builtin_setjmp_frame_value (void)
110  /* Fill structure ix86_frame about frame of currently computed function.  */
111  
112  static void
113 -ix86_compute_frame_layout (struct ix86_frame *frame)
114 +ix86_compute_frame_layout (void)
115  {
116 +  struct ix86_frame *frame = &cfun->machine->frame;
117    unsigned HOST_WIDE_INT stack_alignment_needed;
118    HOST_WIDE_INT offset;
119    unsigned HOST_WIDE_INT preferred_alignment;
120 @@ -13737,7 +13692,8 @@ ix86_expand_prologue (void)
121    m->fs.sp_offset = INCOMING_FRAME_SP_OFFSET;
122    m->fs.sp_valid = true;
123  
124 -  ix86_compute_frame_layout (&frame);
125 +  ix86_compute_frame_layout ();
126 +  frame = m->frame;
127  
128    if (!TARGET_64BIT && ix86_function_ms_hook_prologue (current_function_decl))
129      {
130 @@ -14405,7 +14361,8 @@ ix86_expand_epilogue (int style)
131    bool using_drap;
132  
133    ix86_finalize_stack_realign_flags ();
134 -  ix86_compute_frame_layout (&frame);
135 +  ix86_compute_frame_layout ();
136 +  frame = m->frame;
137  
138    m->fs.sp_valid = (!frame_pointer_needed
139                     || (crtl->sp_is_unchanging
140 @@ -14915,7 +14872,8 @@ ix86_expand_split_stack_prologue (void)
141    gcc_assert (flag_split_stack && reload_completed);
142  
143    ix86_finalize_stack_realign_flags ();
144 -  ix86_compute_frame_layout (&frame);
145 +  ix86_compute_frame_layout ();
146 +  frame = cfun->machine->frame;
147    allocate = frame.stack_pointer_offset - INCOMING_FRAME_SP_OFFSET;
148  
149    /* This is the label we will branch to if we have enough stack
150 diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
151 index 9c776dc5172..f9b91286a01 100644
152 --- a/gcc/config/i386/i386.h
153 +++ b/gcc/config/i386/i386.h
154 @@ -2451,9 +2451,56 @@ enum avx_u128_state
155  \f
156  #define FASTCALL_PREFIX '@'
157  \f
158 +#ifndef USED_FOR_TARGET
159 +/* Structure describing stack frame layout.
160 +   Stack grows downward:
161 +
162 +   [arguments]
163 +                                       <- ARG_POINTER
164 +   saved pc
165 +
166 +   saved static chain                  if ix86_static_chain_on_stack
167 +
168 +   saved frame pointer                 if frame_pointer_needed
169 +                                       <- HARD_FRAME_POINTER
170 +   [saved regs]
171 +                                       <- regs_save_offset
172 +   [padding0]
173 +
174 +   [saved SSE regs]
175 +                                       <- sse_regs_save_offset
176 +   [padding1]          |
177 +                      |                <- FRAME_POINTER
178 +   [va_arg registers]  |
179 +                      |
180 +   [frame]            |
181 +                      |
182 +   [padding2]         | = to_allocate
183 +                                       <- STACK_POINTER
184 +  */
185 +struct GTY(()) ix86_frame
186 +{
187 +  int nsseregs;
188 +  int nregs;
189 +  int va_arg_size;
190 +  int red_zone_size;
191 +  int outgoing_arguments_size;
192 +
193 +  /* The offsets relative to ARG_POINTER.  */
194 +  HOST_WIDE_INT frame_pointer_offset;
195 +  HOST_WIDE_INT hard_frame_pointer_offset;
196 +  HOST_WIDE_INT stack_pointer_offset;
197 +  HOST_WIDE_INT hfp_save_offset;
198 +  HOST_WIDE_INT reg_save_offset;
199 +  HOST_WIDE_INT sse_reg_save_offset;
200 +
201 +  /* When save_regs_using_mov is set, emit prologue using
202 +     move instead of push instructions.  */
203 +  bool save_regs_using_mov;
204 +};
205 +
206  /* Machine specific frame tracking during prologue/epilogue generation.  */
207  
208 -#ifndef USED_FOR_TARGET
209  struct GTY(()) machine_frame_state
210  {
211    /* This pair tracks the currently active CFA as reg+offset.  When reg
212 @@ -2512,6 +2559,9 @@ struct GTY(()) machine_function {
213    int varargs_fpr_size;
214    int optimize_mode_switching[MAX_386_ENTITIES];
215  
216 +  /* Cached initial frame layout for the current function.  */
217 +  struct ix86_frame frame;
218 +
219    /* Number of saved registers USE_FAST_PROLOGUE_EPILOGUE
220       has been computed for.  */
221    int use_fast_prologue_epilogue_nregs;
222 @@ -2594,6 +2644,7 @@ struct GTY(()) machine_function {
223  #define ix86_current_function_calls_tls_descriptor \
224    (ix86_tls_descriptor_calls_expanded_in_cfun && df_regs_ever_live_p (SP_REG))
225  #define ix86_static_chain_on_stack (cfun->machine->static_chain_on_stack)
226 +#define ix86_red_zone_size (cfun->machine->frame.red_zone_size)
227  
228  /* Control behavior of x86_file_start.  */
229  #define X86_FILE_START_VERSION_DIRECTIVE false
230
231 commit b721283e4f4ff378a0bee2255b7d62163eab9f1e
232 Author: hjl <hjl@138bc75d-0d04-0410-961f-82ee72b054a4>
233 Date:   Mon Nov 6 23:04:15 2017 +0000
234
235     i386: Use reference of struct ix86_frame to avoid copy
236     
237     When there is no need to make a copy of ix86_frame, we can use reference
238     of struct ix86_frame to avoid copy.
239     
240     Tested on x86-64.
241     
242             * config/i386/i386.c (ix86_can_use_return_insn_p): Use reference
243             of struct ix86_frame.
244             (ix86_initial_elimination_offset): Likewise.
245             (ix86_expand_split_stack_prologue): Likewise.
246     
247     git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@254480 138bc75d-0d04-0410-961f-82ee72b054a4
248
249 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
250 index 813337242d8..397ef7cac26 100644
251 --- a/gcc/config/i386/i386.c
252 +++ b/gcc/config/i386/i386.c
253 @@ -11843,8 +11843,6 @@ symbolic_reference_mentioned_p (rtx op)
254  bool
255  ix86_can_use_return_insn_p (void)
256  {
257 -  struct ix86_frame frame;
258 -
259    /* Don't use `ret' instruction in interrupt handler.  */
260    if (! reload_completed
261        || frame_pointer_needed
262 @@ -11857,7 +11855,7 @@ ix86_can_use_return_insn_p (void)
263      return 0;
264  
265    ix86_compute_frame_layout ();
266 -  frame = cfun->machine->frame;
267 +  struct ix86_frame &frame = cfun->machine->frame;
268    return (frame.stack_pointer_offset == UNITS_PER_WORD
269           && (frame.nregs + frame.nsseregs) == 0);
270  }
271 @@ -12344,7 +12342,7 @@ HOST_WIDE_INT
272  ix86_initial_elimination_offset (int from, int to)
273  {
274    ix86_compute_frame_layout ();
275 -  struct ix86_frame frame = cfun->machine->frame;
276 +  struct ix86_frame &frame = cfun->machine->frame;
277  
278    if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
279      return frame.hard_frame_pointer_offset;
280 @@ -14860,7 +14858,6 @@ static GTY(()) rtx split_stack_fn_large;
281  void
282  ix86_expand_split_stack_prologue (void)
283  {
284 -  struct ix86_frame frame;
285    HOST_WIDE_INT allocate;
286    unsigned HOST_WIDE_INT args_size;
287    rtx_code_label *label;
288 @@ -14873,7 +14870,7 @@ ix86_expand_split_stack_prologue (void)
289  
290    ix86_finalize_stack_realign_flags ();
291    ix86_compute_frame_layout ();
292 -  frame = cfun->machine->frame;
293 +  struct ix86_frame &frame = cfun->machine->frame;
294    allocate = frame.stack_pointer_offset - INCOMING_FRAME_SP_OFFSET;
295  
296    /* This is the label we will branch to if we have enough stack
297
298 commit 3b89cfddd6276d3f13c210ed11ef638515392a04
299 Author: H.J. Lu <hjl.tools@gmail.com>
300 Date:   Tue Nov 28 10:26:35 2017 -0800
301
302     i386: More use reference of struct ix86_frame to avoid copy
303     
304     When there is no need to make a copy of ix86_frame, we can use reference
305     of struct ix86_frame to avoid copy.
306     
307             * config/i386/i386.c (ix86_expand_prologue): Use reference of
308             struct ix86_frame.
309             (ix86_expand_epilogue): Likewise.
310
311 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
312 index 397ef7cac26..986e6d79584 100644
313 --- a/gcc/config/i386/i386.c
314 +++ b/gcc/config/i386/i386.c
315 @@ -13667,7 +13667,6 @@ ix86_expand_prologue (void)
316  {
317    struct machine_function *m = cfun->machine;
318    rtx insn, t;
319 -  struct ix86_frame frame;
320    HOST_WIDE_INT allocate;
321    bool int_registers_saved;
322    bool sse_registers_saved;
323 @@ -13691,7 +13690,7 @@ ix86_expand_prologue (void)
324    m->fs.sp_valid = true;
325  
326    ix86_compute_frame_layout ();
327 -  frame = m->frame;
328 +  struct ix86_frame &frame = cfun->machine->frame;
329  
330    if (!TARGET_64BIT && ix86_function_ms_hook_prologue (current_function_decl))
331      {
332 @@ -14354,13 +14353,12 @@ ix86_expand_epilogue (int style)
333  {
334    struct machine_function *m = cfun->machine;
335    struct machine_frame_state frame_state_save = m->fs;
336 -  struct ix86_frame frame;
337    bool restore_regs_via_mov;
338    bool using_drap;
339  
340    ix86_finalize_stack_realign_flags ();
341    ix86_compute_frame_layout ();
342 -  frame = m->frame;
343 +  struct ix86_frame &frame = cfun->machine->frame;
344  
345    m->fs.sp_valid = (!frame_pointer_needed
346                     || (crtl->sp_is_unchanging
347
348 commit c89890ab6730606ecc8d3f5937fe352341f0f713
349 Author: H.J. Lu <hjl.tools@gmail.com>
350 Date:   Sat Jan 6 22:29:55 2018 -0800
351
352     x86: Add -mindirect-branch=
353     
354     Add -mindirect-branch= option to convert indirect call and jump to call
355     and return thunks.  The default is 'keep', which keeps indirect call and
356     jump unmodified.  'thunk' converts indirect call and jump to call and
357     return thunk.  'thunk-inline' converts indirect call and jump to inlined
358     call and return thunk.  'thunk-extern' converts indirect call and jump to
359     external call and return thunk provided in a separate object file.  You
360     can control this behavior for a specific function by using the function
361     attribute indirect_branch.
362     
363     2 kinds of thunks are geneated.  Memory thunk where the function address
364     is at the top of the stack:
365     
366     __x86_indirect_thunk:
367             call L2
368     L1:
369             pause
370             lfence
371             jmp L1
372     L2:
373             lea 8(%rsp), %rsp|lea 4(%esp), %esp
374             ret
375     
376     Indirect jmp via memory, "jmp mem", is converted to
377     
378             push memory
379             jmp __x86_indirect_thunk
380     
381     Indirect call via memory, "call mem", is converted to
382     
383             jmp L2
384     L1:
385             push [mem]
386             jmp __x86_indirect_thunk
387     L2:
388             call L1
389     
390     Register thunk where the function address is in a register, reg:
391     
392     __x86_indirect_thunk_reg:
393             call    L2
394     L1:
395             pause
396             lfence
397             jmp     L1
398     L2:
399             movq    %reg, (%rsp)|movl    %reg, (%esp)
400             ret
401     
402     where reg is one of (r|e)ax, (r|e)dx, (r|e)cx, (r|e)bx, (r|e)si, (r|e)di,
403     (r|e)bp, r8, r9, r10, r11, r12, r13, r14 and r15.
404     
405     Indirect jmp via register, "jmp reg", is converted to
406     
407             jmp __x86_indirect_thunk_reg
408     
409     Indirect call via register, "call reg", is converted to
410     
411             call __x86_indirect_thunk_reg
412     
413     gcc/
414     
415             * config/i386/i386-opts.h (indirect_branch): New.
416             * config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
417             * config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
418             with local indirect jump when converting indirect call and jump.
419             (ix86_set_indirect_branch_type): New.
420             (ix86_set_current_function): Call ix86_set_indirect_branch_type.
421             (indirectlabelno): New.
422             (indirect_thunk_needed): Likewise.
423             (indirect_thunk_bnd_needed): Likewise.
424             (indirect_thunks_used): Likewise.
425             (indirect_thunks_bnd_used): Likewise.
426             (INDIRECT_LABEL): Likewise.
427             (indirect_thunk_name): Likewise.
428             (output_indirect_thunk): Likewise.
429             (output_indirect_thunk_function): Likewise.
430             (ix86_output_indirect_branch): Likewise.
431             (ix86_output_indirect_jmp): Likewise.
432             (ix86_code_end): Call output_indirect_thunk_function if needed.
433             (ix86_output_call_insn): Call ix86_output_indirect_branch if
434             needed.
435             (ix86_handle_fndecl_attribute): Handle indirect_branch.
436             (ix86_attribute_table): Add indirect_branch.
437             * config/i386/i386.h (machine_function): Add indirect_branch_type
438             and has_local_indirect_jump.
439             * config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
440             to true.
441             (tablejump): Likewise.
442             (*indirect_jump): Use ix86_output_indirect_jmp.
443             (*tablejump_1): Likewise.
444             (simple_return_indirect_internal): Likewise.
445             * config/i386/i386.opt (mindirect-branch=): New option.
446             (indirect_branch): New.
447             (keep): Likewise.
448             (thunk): Likewise.
449             (thunk-inline): Likewise.
450             (thunk-extern): Likewise.
451             * doc/extend.texi: Document indirect_branch function attribute.
452             * doc/invoke.texi: Document -mindirect-branch= option.
453     
454     gcc/testsuite/
455     
456             * gcc.target/i386/indirect-thunk-1.c: New test.
457             * gcc.target/i386/indirect-thunk-2.c: Likewise.
458             * gcc.target/i386/indirect-thunk-3.c: Likewise.
459             * gcc.target/i386/indirect-thunk-4.c: Likewise.
460             * gcc.target/i386/indirect-thunk-5.c: Likewise.
461             * gcc.target/i386/indirect-thunk-6.c: Likewise.
462             * gcc.target/i386/indirect-thunk-7.c: Likewise.
463             * gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
464             * gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
465             * gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
466             * gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
467             * gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
468             * gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
469             * gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
470             * gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
471             * gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
472             * gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
473             * gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
474             * gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
475             * gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
476             * gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
477             * gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
478             * gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
479             * gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
480             * gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
481             * gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
482             * gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
483             * gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
484             * gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
485             * gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
486             * gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
487             * gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
488             * gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
489
490 diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h
491 index 542cd0f3d67..efcdc3b1a14 100644
492 --- a/gcc/config/i386/i386-opts.h
493 +++ b/gcc/config/i386/i386-opts.h
494 @@ -99,4 +99,17 @@ enum stack_protector_guard {
495    SSP_GLOBAL    /* global canary */
496  };
497  
498 +/* This is used to mitigate variant #2 of the speculative execution
499 +   vulnerabilities on x86 processors identified by CVE-2017-5715, aka
500 +   Spectre.  They convert indirect branches and function returns to
501 +   call and return thunks to avoid speculative execution via indirect
502 +   call, jmp and ret.  */
503 +enum indirect_branch {
504 +  indirect_branch_unset = 0,
505 +  indirect_branch_keep,
506 +  indirect_branch_thunk,
507 +  indirect_branch_thunk_inline,
508 +  indirect_branch_thunk_extern
509 +};
510 +
511  #endif
512 diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
513 index d2cccf14735..bcdd9872db9 100644
514 --- a/gcc/config/i386/i386-protos.h
515 +++ b/gcc/config/i386/i386-protos.h
516 @@ -313,6 +313,7 @@ extern enum attr_cpu ix86_schedule;
517  #endif
518  
519  extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
520 +extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
521  extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
522                                                 enum machine_mode mode);
523  
524 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
525 index 986e6d79584..f1c58faa035 100644
526 --- a/gcc/config/i386/i386.c
527 +++ b/gcc/config/i386/i386.c
528 @@ -4212,12 +4212,23 @@ make_pass_stv (gcc::context *ctxt)
529    return new pass_stv (ctxt);
530  }
531  
532 -/* Return true if a red-zone is in use.  */
533 +/* Return true if a red-zone is in use.  We can't use red-zone when
534 +   there are local indirect jumps, like "indirect_jump" or "tablejump",
535 +   which jumps to another place in the function, since "call" in the
536 +   indirect thunk pushes the return address onto stack, destroying
537 +   red-zone.
538 +
539 +   TODO: If we can reserve the first 2 WORDs, for PUSH and, another
540 +   for CALL, in red-zone, we can allow local indirect jumps with
541 +   indirect thunk.  */
542  
543  bool
544  ix86_using_red_zone (void)
545  {
546 -  return TARGET_RED_ZONE && !TARGET_64BIT_MS_ABI;
547 +  return (TARGET_RED_ZONE
548 +         && !TARGET_64BIT_MS_ABI
549 +         && (!cfun->machine->has_local_indirect_jump
550 +             || cfun->machine->indirect_branch_type == indirect_branch_keep));
551  }
552  \f
553  /* Return a string that documents the current -m options.  The caller is
554 @@ -7148,6 +7159,37 @@ ix86_set_func_type (tree fndecl)
555      }
556  }
557  
558 +/* Set the indirect_branch_type field from the function FNDECL.  */
559 +
560 +static void
561 +ix86_set_indirect_branch_type (tree fndecl)
562 +{
563 +  if (cfun->machine->indirect_branch_type == indirect_branch_unset)
564 +    {
565 +      tree attr = lookup_attribute ("indirect_branch",
566 +                                   DECL_ATTRIBUTES (fndecl));
567 +      if (attr != NULL)
568 +       {
569 +         tree args = TREE_VALUE (attr);
570 +         if (args == NULL)
571 +           gcc_unreachable ();
572 +         tree cst = TREE_VALUE (args);
573 +         if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0)
574 +           cfun->machine->indirect_branch_type = indirect_branch_keep;
575 +         else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0)
576 +           cfun->machine->indirect_branch_type = indirect_branch_thunk;
577 +         else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0)
578 +           cfun->machine->indirect_branch_type = indirect_branch_thunk_inline;
579 +         else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0)
580 +           cfun->machine->indirect_branch_type = indirect_branch_thunk_extern;
581 +         else
582 +           gcc_unreachable ();
583 +       }
584 +      else
585 +       cfun->machine->indirect_branch_type = ix86_indirect_branch;
586 +    }
587 +}
588 +
589  /* Establish appropriate back-end context for processing the function
590     FNDECL.  The argument might be NULL to indicate processing at top
591     level, outside of any function scope.  */
592 @@ -7163,7 +7205,10 @@ ix86_set_current_function (tree fndecl)
593          one is extern inline and one isn't.  Call ix86_set_func_type
594          to set the func_type field.  */
595        if (fndecl != NULL_TREE)
596 -       ix86_set_func_type (fndecl);
597 +       {
598 +         ix86_set_func_type (fndecl);
599 +         ix86_set_indirect_branch_type (fndecl);
600 +       }
601        return;
602      }
603  
604 @@ -7183,6 +7228,7 @@ ix86_set_current_function (tree fndecl)
605      }
606  
607    ix86_set_func_type (fndecl);
608 +  ix86_set_indirect_branch_type (fndecl);
609  
610    tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
611    if (new_tree == NULL_TREE)
612 @@ -11920,6 +11966,220 @@ ix86_setup_frame_addresses (void)
613  # endif
614  #endif
615  
616 +/* Label count for call and return thunks.  It is used to make unique
617 +   labels in call and return thunks.  */
618 +static int indirectlabelno;
619 +
620 +/* True if call and return thunk functions are needed.  */
621 +static bool indirect_thunk_needed = false;
622 +/* True if call and return thunk functions with the BND prefix are
623 +   needed.  */
624 +static bool indirect_thunk_bnd_needed = false;
625 +
626 +/* Bit masks of integer registers, which contain branch target, used
627 +   by call and return thunks functions.  */
628 +static int indirect_thunks_used;
629 +/* Bit masks of integer registers, which contain branch target, used
630 +   by call and return thunks functions with the BND prefix.  */
631 +static int indirect_thunks_bnd_used;
632 +
633 +#ifndef INDIRECT_LABEL
634 +# define INDIRECT_LABEL "LIND"
635 +#endif
636 +
637 +/* Fills in the label name that should be used for the indirect thunk.  */
638 +
639 +static void
640 +indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
641 +{
642 +  if (USE_HIDDEN_LINKONCE)
643 +    {
644 +      const char *bnd = need_bnd_p ? "_bnd" : "";
645 +      if (regno >= 0)
646 +       {
647 +         const char *reg_prefix;
648 +         if (LEGACY_INT_REGNO_P (regno))
649 +           reg_prefix = TARGET_64BIT ? "r" : "e";
650 +         else
651 +           reg_prefix = "";
652 +         sprintf (name, "__x86_indirect_thunk%s_%s%s",
653 +                  bnd, reg_prefix, reg_names[regno]);
654 +       }
655 +      else
656 +       sprintf (name, "__x86_indirect_thunk%s", bnd);
657 +    }
658 +  else
659 +    {
660 +      if (regno >= 0)
661 +       {
662 +         if (need_bnd_p)
663 +           ASM_GENERATE_INTERNAL_LABEL (name, "LITBR", regno);
664 +         else
665 +           ASM_GENERATE_INTERNAL_LABEL (name, "LITR", regno);
666 +       }
667 +      else
668 +       {
669 +         if (need_bnd_p)
670 +           ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
671 +         else
672 +           ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
673 +       }
674 +    }
675 +}
676 +
677 +/* Output a call and return thunk for indirect branch.  If BND_P is
678 +   true, the BND prefix is needed.   If REGNO != -1,  the function
679 +   address is in REGNO and the call and return thunk looks like:
680 +
681 +       call    L2
682 +   L1:
683 +       pause
684 +       jmp     L1
685 +   L2:
686 +       mov     %REG, (%sp)
687 +       ret
688 +
689 +   Otherwise, the function address is on the top of stack and the
690 +   call and return thunk looks like:
691 +
692 +       call L2
693 +  L1:
694 +       pause
695 +       jmp L1
696 +  L2:
697 +       lea WORD_SIZE(%sp), %sp
698 +       ret
699 + */
700 +
701 +static void
702 +output_indirect_thunk (bool need_bnd_p, int regno)
703 +{
704 +  char indirectlabel1[32];
705 +  char indirectlabel2[32];
706 +
707 +  ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, INDIRECT_LABEL,
708 +                              indirectlabelno++);
709 +  ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, INDIRECT_LABEL,
710 +                              indirectlabelno++);
711 +
712 +  /* Call */
713 +  if (need_bnd_p)
714 +    fputs ("\tbnd call\t", asm_out_file);
715 +  else
716 +    fputs ("\tcall\t", asm_out_file);
717 +  assemble_name_raw (asm_out_file, indirectlabel2);
718 +  fputc ('\n', asm_out_file);
719 +
720 +  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
721 +
722 +  /* Pause + lfence.  */
723 +  fprintf (asm_out_file, "\tpause\n\tlfence\n");
724 +
725 +  /* Jump.  */
726 +  fputs ("\tjmp\t", asm_out_file);
727 +  assemble_name_raw (asm_out_file, indirectlabel1);
728 +  fputc ('\n', asm_out_file);
729 +
730 +  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
731 +
732 +  if (regno >= 0)
733 +    {
734 +      /* MOV.  */
735 +      rtx xops[2];
736 +      xops[0] = gen_rtx_MEM (word_mode, stack_pointer_rtx);
737 +      xops[1] = gen_rtx_REG (word_mode, regno);
738 +      output_asm_insn ("mov\t{%1, %0|%0, %1}", xops);
739 +    }
740 +  else
741 +    {
742 +      /* LEA.  */
743 +      rtx xops[2];
744 +      xops[0] = stack_pointer_rtx;
745 +      xops[1] = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD);
746 +      output_asm_insn ("lea\t{%E1, %0|%0, %E1}", xops);
747 +    }
748 +
749 +  if (need_bnd_p)
750 +    fputs ("\tbnd ret\n", asm_out_file);
751 +  else
752 +    fputs ("\tret\n", asm_out_file);
753 +}
754 +
755 +/* Output a funtion with a call and return thunk for indirect branch.
756 +   If BND_P is true, the BND prefix is needed.   If REGNO != -1,  the
757 +   function address is in REGNO.  Otherwise, the function address is
758 +   on the top of stack.  */
759 +
760 +static void
761 +output_indirect_thunk_function (bool need_bnd_p, int regno)
762 +{
763 +  char name[32];
764 +  tree decl;
765 +
766 +  /* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd.  */
767 +  indirect_thunk_name (name, regno, need_bnd_p);
768 +  decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
769 +                    get_identifier (name),
770 +                    build_function_type_list (void_type_node, NULL_TREE));
771 +  DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
772 +                                  NULL_TREE, void_type_node);
773 +  TREE_PUBLIC (decl) = 1;
774 +  TREE_STATIC (decl) = 1;
775 +  DECL_IGNORED_P (decl) = 1;
776 +
777 +#if TARGET_MACHO
778 +  if (TARGET_MACHO)
779 +    {
780 +      switch_to_section (darwin_sections[picbase_thunk_section]);
781 +      fputs ("\t.weak_definition\t", asm_out_file);
782 +      assemble_name (asm_out_file, name);
783 +      fputs ("\n\t.private_extern\t", asm_out_file);
784 +      assemble_name (asm_out_file, name);
785 +      putc ('\n', asm_out_file);
786 +      ASM_OUTPUT_LABEL (asm_out_file, name);
787 +      DECL_WEAK (decl) = 1;
788 +    }
789 +  else
790 +#endif
791 +    if (USE_HIDDEN_LINKONCE)
792 +      {
793 +       cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl));
794 +
795 +       targetm.asm_out.unique_section (decl, 0);
796 +       switch_to_section (get_named_section (decl, NULL, 0));
797 +
798 +       targetm.asm_out.globalize_label (asm_out_file, name);
799 +       fputs ("\t.hidden\t", asm_out_file);
800 +       assemble_name (asm_out_file, name);
801 +       putc ('\n', asm_out_file);
802 +       ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl);
803 +      }
804 +    else
805 +      {
806 +       switch_to_section (text_section);
807 +       ASM_OUTPUT_LABEL (asm_out_file, name);
808 +      }
809 +
810 +  DECL_INITIAL (decl) = make_node (BLOCK);
811 +  current_function_decl = decl;
812 +  allocate_struct_function (decl, false);
813 +  init_function_start (decl);
814 +  /* We're about to hide the function body from callees of final_* by
815 +     emitting it directly; tell them we're a thunk, if they care.  */
816 +  cfun->is_thunk = true;
817 +  first_function_block_is_cold = false;
818 +  /* Make sure unwind info is emitted for the thunk if needed.  */
819 +  final_start_function (emit_barrier (), asm_out_file, 1);
820 +
821 +  output_indirect_thunk (need_bnd_p, regno);
822 +
823 +  final_end_function ();
824 +  init_insn_lengths ();
825 +  free_after_compilation (cfun);
826 +  set_cfun (NULL);
827 +  current_function_decl = NULL;
828 +}
829 +
830  static int pic_labels_used;
831  
832  /* Fills in the label name that should be used for a pc thunk for
833 @@ -11946,11 +12206,32 @@ ix86_code_end (void)
834    rtx xops[2];
835    int regno;
836  
837 +  if (indirect_thunk_needed)
838 +    output_indirect_thunk_function (false, -1);
839 +  if (indirect_thunk_bnd_needed)
840 +    output_indirect_thunk_function (true, -1);
841 +
842 +  for (regno = FIRST_REX_INT_REG; regno <= LAST_REX_INT_REG; regno++)
843 +    {
844 +      int i = regno - FIRST_REX_INT_REG + LAST_INT_REG + 1;
845 +      if ((indirect_thunks_used & (1 << i)))
846 +       output_indirect_thunk_function (false, regno);
847 +
848 +      if ((indirect_thunks_bnd_used & (1 << i)))
849 +       output_indirect_thunk_function (true, regno);
850 +    }
851 +
852    for (regno = AX_REG; regno <= SP_REG; regno++)
853      {
854        char name[32];
855        tree decl;
856  
857 +      if ((indirect_thunks_used & (1 << regno)))
858 +       output_indirect_thunk_function (false, regno);
859 +
860 +      if ((indirect_thunks_bnd_used & (1 << regno)))
861 +       output_indirect_thunk_function (true, regno);
862 +
863        if (!(pic_labels_used & (1 << regno)))
864         continue;
865  
866 @@ -28446,12 +28727,292 @@ ix86_nopic_noplt_attribute_p (rtx call_op)
867    return false;
868  }
869  
870 +/* Output indirect branch via a call and return thunk.  CALL_OP is a
871 +   register which contains the branch target.  XASM is the assembly
872 +   template for CALL_OP.  Branch is a tail call if SIBCALL_P is true.
873 +   A normal call is converted to:
874 +
875 +       call __x86_indirect_thunk_reg
876 +
877 +   and a tail call is converted to:
878 +
879 +       jmp __x86_indirect_thunk_reg
880 + */
881 +
882 +static void
883 +ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
884 +{
885 +  char thunk_name_buf[32];
886 +  char *thunk_name;
887 +  bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
888 +  int regno = REGNO (call_op);
889 +
890 +  if (cfun->machine->indirect_branch_type
891 +      != indirect_branch_thunk_inline)
892 +    {
893 +      if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
894 +       {
895 +         int i = regno;
896 +         if (i >= FIRST_REX_INT_REG)
897 +           i -= (FIRST_REX_INT_REG - LAST_INT_REG - 1);
898 +         if (need_bnd_p)
899 +           indirect_thunks_bnd_used |= 1 << i;
900 +         else
901 +           indirect_thunks_used |= 1 << i;
902 +       }
903 +      indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
904 +      thunk_name = thunk_name_buf;
905 +    }
906 +  else
907 +    thunk_name = NULL;
908 +
909 +  if (sibcall_p)
910 +    {
911 +      if (thunk_name != NULL)
912 +       {
913 +         if (need_bnd_p)
914 +           fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
915 +         else
916 +           fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
917 +       }
918 +      else
919 +       output_indirect_thunk (need_bnd_p, regno);
920 +    }
921 +  else
922 +    {
923 +      if (thunk_name != NULL)
924 +       {
925 +         if (need_bnd_p)
926 +           fprintf (asm_out_file, "\tbnd call\t%s\n", thunk_name);
927 +         else
928 +           fprintf (asm_out_file, "\tcall\t%s\n", thunk_name);
929 +         return;
930 +       }
931 +
932 +      char indirectlabel1[32];
933 +      char indirectlabel2[32];
934 +
935 +      ASM_GENERATE_INTERNAL_LABEL (indirectlabel1,
936 +                                  INDIRECT_LABEL,
937 +                                  indirectlabelno++);
938 +      ASM_GENERATE_INTERNAL_LABEL (indirectlabel2,
939 +                                  INDIRECT_LABEL,
940 +                                  indirectlabelno++);
941 +
942 +      /* Jump.  */
943 +      if (need_bnd_p)
944 +       fputs ("\tbnd jmp\t", asm_out_file);
945 +      else
946 +       fputs ("\tjmp\t", asm_out_file);
947 +      assemble_name_raw (asm_out_file, indirectlabel2);
948 +      fputc ('\n', asm_out_file);
949 +
950 +      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
951 +
952 +      if (thunk_name != NULL)
953 +       {
954 +         if (need_bnd_p)
955 +           fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
956 +         else
957 +           fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
958 +       }
959 +      else
960 +       output_indirect_thunk (need_bnd_p, regno);
961 +
962 +      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
963 +
964 +      /* Call.  */
965 +      if (need_bnd_p)
966 +       fputs ("\tbnd call\t", asm_out_file);
967 +      else
968 +       fputs ("\tcall\t", asm_out_file);
969 +      assemble_name_raw (asm_out_file, indirectlabel1);
970 +      fputc ('\n', asm_out_file);
971 +    }
972 +}
973 +
974 +/* Output indirect branch via a call and return thunk.  CALL_OP is
975 +   the branch target.  XASM is the assembly template for CALL_OP.
976 +   Branch is a tail call if SIBCALL_P is true.  A normal call is
977 +   converted to:
978 +
979 +       jmp L2
980 +   L1:
981 +       push CALL_OP
982 +       jmp __x86_indirect_thunk
983 +   L2:
984 +       call L1
985 +
986 +   and a tail call is converted to:
987 +
988 +       push CALL_OP
989 +       jmp __x86_indirect_thunk
990 + */
991 +
992 +static void
993 +ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
994 +                                     bool sibcall_p)
995 +{
996 +  char thunk_name_buf[32];
997 +  char *thunk_name;
998 +  char push_buf[64];
999 +  bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
1000 +  int regno = -1;
1001 +
1002 +  if (cfun->machine->indirect_branch_type
1003 +      != indirect_branch_thunk_inline)
1004 +    {
1005 +      if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
1006 +       {
1007 +         if (need_bnd_p)
1008 +           indirect_thunk_bnd_needed = true;
1009 +         else
1010 +           indirect_thunk_needed = true;
1011 +       }
1012 +      indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
1013 +      thunk_name = thunk_name_buf;
1014 +    }
1015 +  else
1016 +    thunk_name = NULL;
1017 +
1018 +  snprintf (push_buf, sizeof (push_buf), "push{%c}\t%s",
1019 +           TARGET_64BIT ? 'q' : 'l', xasm);
1020 +
1021 +  if (sibcall_p)
1022 +    {
1023 +      output_asm_insn (push_buf, &call_op);
1024 +      if (thunk_name != NULL)
1025 +       {
1026 +         if (need_bnd_p)
1027 +           fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
1028 +         else
1029 +           fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
1030 +       }
1031 +      else
1032 +       output_indirect_thunk (need_bnd_p, regno);
1033 +    }
1034 +  else
1035 +    {
1036 +      char indirectlabel1[32];
1037 +      char indirectlabel2[32];
1038 +
1039 +      ASM_GENERATE_INTERNAL_LABEL (indirectlabel1,
1040 +                                  INDIRECT_LABEL,
1041 +                                  indirectlabelno++);
1042 +      ASM_GENERATE_INTERNAL_LABEL (indirectlabel2,
1043 +                                  INDIRECT_LABEL,
1044 +                                  indirectlabelno++);
1045 +
1046 +      /* Jump.  */
1047 +      if (need_bnd_p)
1048 +       fputs ("\tbnd jmp\t", asm_out_file);
1049 +      else
1050 +       fputs ("\tjmp\t", asm_out_file);
1051 +      assemble_name_raw (asm_out_file, indirectlabel2);
1052 +      fputc ('\n', asm_out_file);
1053 +
1054 +      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
1055 +
1056 +      /* An external function may be called via GOT, instead of PLT.  */
1057 +      if (MEM_P (call_op))
1058 +       {
1059 +         struct ix86_address parts;
1060 +         rtx addr = XEXP (call_op, 0);
1061 +         if (ix86_decompose_address (addr, &parts)
1062 +             && parts.base == stack_pointer_rtx)
1063 +           {
1064 +             /* Since call will adjust stack by -UNITS_PER_WORD,
1065 +                we must convert "disp(stack, index, scale)" to
1066 +                "disp+UNITS_PER_WORD(stack, index, scale)".  */
1067 +             if (parts.index)
1068 +               {
1069 +                 addr = gen_rtx_MULT (Pmode, parts.index,
1070 +                                      GEN_INT (parts.scale));
1071 +                 addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
1072 +                                      addr);
1073 +               }
1074 +             else
1075 +               addr = stack_pointer_rtx;
1076 +
1077 +             rtx disp;
1078 +             if (parts.disp != NULL_RTX)
1079 +               disp = plus_constant (Pmode, parts.disp,
1080 +                                     UNITS_PER_WORD);
1081 +             else
1082 +               disp = GEN_INT (UNITS_PER_WORD);
1083 +
1084 +             addr = gen_rtx_PLUS (Pmode, addr, disp);
1085 +             call_op = gen_rtx_MEM (GET_MODE (call_op), addr);
1086 +           }
1087 +       }
1088 +
1089 +      output_asm_insn (push_buf, &call_op);
1090 +
1091 +      if (thunk_name != NULL)
1092 +       {
1093 +         if (need_bnd_p)
1094 +           fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
1095 +         else
1096 +           fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
1097 +       }
1098 +      else
1099 +       output_indirect_thunk (need_bnd_p, regno);
1100 +
1101 +      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
1102 +
1103 +      /* Call.  */
1104 +      if (need_bnd_p)
1105 +       fputs ("\tbnd call\t", asm_out_file);
1106 +      else
1107 +       fputs ("\tcall\t", asm_out_file);
1108 +      assemble_name_raw (asm_out_file, indirectlabel1);
1109 +      fputc ('\n', asm_out_file);
1110 +    }
1111 +}
1112 +
1113 +/* Output indirect branch via a call and return thunk.  CALL_OP is
1114 +   the branch target.  XASM is the assembly template for CALL_OP.
1115 +   Branch is a tail call if SIBCALL_P is true.   */
1116 +
1117 +static void
1118 +ix86_output_indirect_branch (rtx call_op, const char *xasm,
1119 +                            bool sibcall_p)
1120 +{
1121 +  if (REG_P (call_op))
1122 +    ix86_output_indirect_branch_via_reg (call_op, sibcall_p);
1123 +  else
1124 +    ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p);
1125 +}
1126 +/* Output indirect jump.  CALL_OP is the jump target.  Jump is a
1127 +   function return if RET_P is true.  */
1128 +
1129 +const char *
1130 +ix86_output_indirect_jmp (rtx call_op, bool ret_p)
1131 +{
1132 +  if (cfun->machine->indirect_branch_type != indirect_branch_keep)
1133 +    {
1134 +      /* We can't have red-zone if this isn't a function return since
1135 +        "call" in the indirect thunk pushes the return address onto
1136 +        stack, destroying red-zone.  */
1137 +      if (!ret_p && ix86_red_zone_size != 0)
1138 +       gcc_unreachable ();
1139 +
1140 +      ix86_output_indirect_branch (call_op, "%0", true);
1141 +      return "";
1142 +    }
1143 +  else
1144 +    return "%!jmp\t%A0";
1145 +}
1146 +
1147  /* Output the assembly for a call instruction.  */
1148  
1149  const char *
1150  ix86_output_call_insn (rtx_insn *insn, rtx call_op)
1151  {
1152    bool direct_p = constant_call_address_operand (call_op, VOIDmode);
1153 +  bool output_indirect_p
1154 +    = (!TARGET_SEH
1155 +       && cfun->machine->indirect_branch_type != indirect_branch_keep);
1156    bool seh_nop_p = false;
1157    const char *xasm;
1158  
1159 @@ -28461,10 +29022,21 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
1160         {
1161           if (ix86_nopic_noplt_attribute_p (call_op))
1162             {
1163 +             direct_p = false;
1164               if (TARGET_64BIT)
1165 -               xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
1166 +               {
1167 +                 if (output_indirect_p)
1168 +                   xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
1169 +                 else
1170 +                   xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
1171 +               }
1172               else
1173 -               xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
1174 +               {
1175 +                 if (output_indirect_p)
1176 +                   xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}";
1177 +                 else
1178 +                   xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
1179 +               }
1180             }
1181           else
1182             xasm = "%!jmp\t%P0";
1183 @@ -28474,9 +29046,17 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
1184        else if (TARGET_SEH)
1185         xasm = "%!rex.W jmp\t%A0";
1186        else
1187 -       xasm = "%!jmp\t%A0";
1188 +       {
1189 +         if (output_indirect_p)
1190 +           xasm = "%0";
1191 +         else
1192 +           xasm = "%!jmp\t%A0";
1193 +       }
1194  
1195 -      output_asm_insn (xasm, &call_op);
1196 +      if (output_indirect_p && !direct_p)
1197 +       ix86_output_indirect_branch (call_op, xasm, true);
1198 +      else
1199 +       output_asm_insn (xasm, &call_op);
1200        return "";
1201      }
1202  
1203 @@ -28514,18 +29094,37 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
1204      {
1205        if (ix86_nopic_noplt_attribute_p (call_op))
1206         {
1207 +         direct_p = false;
1208           if (TARGET_64BIT)
1209 -           xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
1210 +           {
1211 +             if (output_indirect_p)
1212 +               xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
1213 +             else
1214 +               xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
1215 +           }
1216           else
1217 -           xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
1218 +           {
1219 +             if (output_indirect_p)
1220 +               xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}";
1221 +             else
1222 +               xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
1223 +           }
1224         }
1225        else
1226         xasm = "%!call\t%P0";
1227      }
1228    else
1229 -    xasm = "%!call\t%A0";
1230 +    {
1231 +      if (output_indirect_p)
1232 +       xasm = "%0";
1233 +      else
1234 +       xasm = "%!call\t%A0";
1235 +    }
1236  
1237 -  output_asm_insn (xasm, &call_op);
1238 +  if (output_indirect_p && !direct_p)
1239 +    ix86_output_indirect_branch (call_op, xasm, false);
1240 +  else
1241 +    output_asm_insn (xasm, &call_op);
1242  
1243    if (seh_nop_p)
1244      return "nop";
1245 @@ -41444,7 +42043,7 @@ ix86_handle_struct_attribute (tree *node, tree name, tree, int,
1246  }
1247  
1248  static tree
1249 -ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
1250 +ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
1251                               bool *no_add_attrs)
1252  {
1253    if (TREE_CODE (*node) != FUNCTION_DECL)
1254 @@ -41453,6 +42052,29 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
1255                 name);
1256        *no_add_attrs = true;
1257      }
1258 +
1259 +  if (is_attribute_p ("indirect_branch", name))
1260 +    {
1261 +      tree cst = TREE_VALUE (args);
1262 +      if (TREE_CODE (cst) != STRING_CST)
1263 +       {
1264 +         warning (OPT_Wattributes,
1265 +                  "%qE attribute requires a string constant argument",
1266 +                  name);
1267 +         *no_add_attrs = true;
1268 +       }
1269 +      else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
1270 +              && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
1271 +              && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
1272 +              && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
1273 +       {
1274 +         warning (OPT_Wattributes,
1275 +                  "argument to %qE attribute is not "
1276 +                  "(keep|thunk|thunk-inline|thunk-extern)", name);
1277 +         *no_add_attrs = true;
1278 +       }
1279 +    }
1280 +
1281    return NULL_TREE;
1282  }
1283  
1284 @@ -45761,6 +46383,8 @@ static const struct attribute_spec ix86_attribute_table[] =
1285      ix86_handle_interrupt_attribute, false },
1286    { "no_caller_saved_registers", 0, 0, false, true, true,
1287      ix86_handle_no_caller_saved_registers_attribute, false },
1288 +  { "indirect_branch", 1, 1, true, false, false,
1289 +    ix86_handle_fndecl_attribute, false },
1290  
1291    /* End element.  */
1292    { NULL,        0, 0, false, false, false, NULL, false }
1293 diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
1294 index f9b91286a01..9d2209e605b 100644
1295 --- a/gcc/config/i386/i386.h
1296 +++ b/gcc/config/i386/i386.h
1297 @@ -2609,6 +2609,13 @@ struct GTY(()) machine_function {
1298    /* Function type.  */
1299    ENUM_BITFIELD(function_type) func_type : 2;
1300  
1301 +  /* How to generate indirec branch.  */
1302 +  ENUM_BITFIELD(indirect_branch) indirect_branch_type : 3;
1303 +
1304 +  /* If true, the current function has local indirect jumps, like
1305 +     "indirect_jump" or "tablejump".  */
1306 +  BOOL_BITFIELD has_local_indirect_jump : 1;
1307 +
1308    /* If true, the current function is a function specified with
1309       the "interrupt" or "no_caller_saved_registers" attribute.  */
1310    BOOL_BITFIELD no_caller_saved_registers : 1;
1311 diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
1312 index 0281bb5f06c..e32f2311065 100644
1313 --- a/gcc/config/i386/i386.md
1314 +++ b/gcc/config/i386/i386.md
1315 @@ -11625,13 +11625,18 @@
1316  {
1317    if (TARGET_X32)
1318      operands[0] = convert_memory_address (word_mode, operands[0]);
1319 +  cfun->machine->has_local_indirect_jump = true;
1320  })
1321  
1322  (define_insn "*indirect_jump"
1323    [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))]
1324    ""
1325 -  "%!jmp\t%A0"
1326 -  [(set_attr "type" "ibr")
1327 +  "* return ix86_output_indirect_jmp (operands[0], false);"
1328 +  [(set (attr "type")
1329 +     (if_then_else (match_test "(cfun->machine->indirect_branch_type
1330 +                                != indirect_branch_keep)")
1331 +       (const_string "multi")
1332 +       (const_string "ibr")))
1333     (set_attr "length_immediate" "0")
1334     (set_attr "maybe_prefix_bnd" "1")])
1335  
1336 @@ -11674,14 +11679,19 @@
1337  
1338    if (TARGET_X32)
1339      operands[0] = convert_memory_address (word_mode, operands[0]);
1340 +  cfun->machine->has_local_indirect_jump = true;
1341  })
1342  
1343  (define_insn "*tablejump_1"
1344    [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))
1345     (use (label_ref (match_operand 1)))]
1346    ""
1347 -  "%!jmp\t%A0"
1348 -  [(set_attr "type" "ibr")
1349 +  "* return ix86_output_indirect_jmp (operands[0], false);"
1350 +  [(set (attr "type")
1351 +     (if_then_else (match_test "(cfun->machine->indirect_branch_type
1352 +                                != indirect_branch_keep)")
1353 +       (const_string "multi")
1354 +       (const_string "ibr")))
1355     (set_attr "length_immediate" "0")
1356     (set_attr "maybe_prefix_bnd" "1")])
1357  \f
1358 @@ -12352,8 +12362,12 @@
1359    [(simple_return)
1360     (use (match_operand:SI 0 "register_operand" "r"))]
1361    "reload_completed"
1362 -  "%!jmp\t%A0"
1363 -  [(set_attr "type" "ibr")
1364 +  "* return ix86_output_indirect_jmp (operands[0], true);"
1365 +  [(set (attr "type")
1366 +     (if_then_else (match_test "(cfun->machine->indirect_branch_type
1367 +                                != indirect_branch_keep)")
1368 +       (const_string "multi")
1369 +       (const_string "ibr")))
1370     (set_attr "length_immediate" "0")
1371     (set_attr "maybe_prefix_bnd" "1")])
1372  
1373 diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
1374 index 9384e29b1de..c076d9c70ab 100644
1375 --- a/gcc/config/i386/i386.opt
1376 +++ b/gcc/config/i386/i386.opt
1377 @@ -927,3 +927,23 @@ Attempt to avoid generating instruction sequences containing ret bytes.
1378  mgeneral-regs-only
1379  Target Report RejectNegative Mask(GENERAL_REGS_ONLY) Var(ix86_target_flags) Save
1380  Generate code which uses only the general registers.
1381 +
1382 +mindirect-branch=
1383 +Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep)
1384 +Convert indirect call and jump to call and return thunks.
1385 +
1386 +Enum
1387 +Name(indirect_branch) Type(enum indirect_branch)
1388 +Known indirect branch choices (for use with the -mindirect-branch= option):
1389 +
1390 +EnumValue
1391 +Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
1392 +
1393 +EnumValue
1394 +Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk)
1395 +
1396 +EnumValue
1397 +Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
1398 +
1399 +EnumValue
1400 +Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
1401 diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
1402 index ba309d01a9b..935381da6fa 100644
1403 --- a/gcc/doc/extend.texi
1404 +++ b/gcc/doc/extend.texi
1405 @@ -5540,6 +5540,16 @@ Specify which floating-point unit to use.  You must specify the
1406  @code{target("fpmath=sse,387")} option as
1407  @code{target("fpmath=sse+387")} because the comma would separate
1408  different options.
1409 +
1410 +@item indirect_branch("@var{choice}")
1411 +@cindex @code{indirect_branch} function attribute, x86
1412 +On x86 targets, the @code{indirect_branch} attribute causes the compiler
1413 +to convert indirect call and jump with @var{choice}.  @samp{keep}
1414 +keeps indirect call and jump unmodified.  @samp{thunk} converts indirect
1415 +call and jump to call and return thunk.  @samp{thunk-inline} converts
1416 +indirect call and jump to inlined call and return thunk.
1417 +@samp{thunk-extern} converts indirect call and jump to external call
1418 +and return thunk provided in a separate object file.
1419  @end table
1420  
1421  On the x86, the inliner does not inline a
1422 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
1423 index 7311c10a754..4979c8c939d 100644
1424 --- a/gcc/doc/invoke.texi
1425 +++ b/gcc/doc/invoke.texi
1426 @@ -1210,7 +1210,8 @@ See RS/6000 and PowerPC Options.
1427  -msse2avx  -mfentry  -mrecord-mcount  -mnop-mcount  -m8bit-idiv @gol
1428  -mavx256-split-unaligned-load  -mavx256-split-unaligned-store @gol
1429  -malign-data=@var{type}  -mstack-protector-guard=@var{guard} @gol
1430 --mmitigate-rop  -mgeneral-regs-only}
1431 +-mmitigate-rop  -mgeneral-regs-only @gol
1432 +-mindirect-branch=@var{choice}}
1433  
1434  @emph{x86 Windows Options}
1435  @gccoptlist{-mconsole  -mcygwin  -mno-cygwin  -mdll @gol
1436 @@ -25686,6 +25687,17 @@ Generate code that uses only the general-purpose registers.  This
1437  prevents the compiler from using floating-point, vector, mask and bound
1438  registers.
1439  
1440 +@item -mindirect-branch=@var{choice}
1441 +@opindex -mindirect-branch
1442 +Convert indirect call and jump with @var{choice}.  The default is
1443 +@samp{keep}, which keeps indirect call and jump unmodified.
1444 +@samp{thunk} converts indirect call and jump to call and return thunk.
1445 +@samp{thunk-inline} converts indirect call and jump to inlined call
1446 +and return thunk.  @samp{thunk-extern} converts indirect call and jump
1447 +to external call and return thunk provided in a separate object file.
1448 +You can control this behavior for a specific function by using the
1449 +function attribute @code{indirect_branch}.  @xref{Function Attributes}.
1450 +
1451  @end table
1452  
1453  These @samp{-m} switches are supported in addition to the above
1454 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
1455 new file mode 100644
1456 index 00000000000..d983e1c3e26
1457 --- /dev/null
1458 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
1459 @@ -0,0 +1,20 @@
1460 +/* { dg-do compile } */
1461 +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
1462 +
1463 +typedef void (*dispatch_t)(long offset);
1464 +
1465 +dispatch_t dispatch;
1466 +
1467 +void
1468 +male_indirect_jump (long offset)
1469 +{
1470 +  dispatch(offset);
1471 +}
1472 +
1473 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1474 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1475 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1476 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1477 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
1478 +/* { dg-final { scan-assembler {\tpause} } } */
1479 +/* { dg-final { scan-assembler {\tlfence} } } */
1480 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
1481 new file mode 100644
1482 index 00000000000..58f09b42d8a
1483 --- /dev/null
1484 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
1485 @@ -0,0 +1,20 @@
1486 +/* { dg-do compile } */
1487 +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
1488 +
1489 +typedef void (*dispatch_t)(long offset);
1490 +
1491 +dispatch_t dispatch[256];
1492 +
1493 +void
1494 +male_indirect_jump (long offset)
1495 +{
1496 +  dispatch[offset](offset);
1497 +}
1498 +
1499 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1500 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1501 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1502 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1503 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
1504 +/* { dg-final { scan-assembler {\tpause} } } */
1505 +/* { dg-final { scan-assembler {\tlfence} } } */
1506 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
1507 new file mode 100644
1508 index 00000000000..f20d35c19b6
1509 --- /dev/null
1510 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
1511 @@ -0,0 +1,21 @@
1512 +/* { dg-do compile } */
1513 +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
1514 +
1515 +typedef void (*dispatch_t)(long offset);
1516 +
1517 +dispatch_t dispatch;
1518 +
1519 +int
1520 +male_indirect_jump (long offset)
1521 +{
1522 +  dispatch(offset);
1523 +  return 0;
1524 +}
1525 +
1526 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1527 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1528 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1529 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1530 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
1531 +/* { dg-final { scan-assembler {\tpause} } } */
1532 +/* { dg-final { scan-assembler {\tlfence} } } */
1533 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
1534 new file mode 100644
1535 index 00000000000..0eff8fb658a
1536 --- /dev/null
1537 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
1538 @@ -0,0 +1,21 @@
1539 +/* { dg-do compile } */
1540 +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
1541 +
1542 +typedef void (*dispatch_t)(long offset);
1543 +
1544 +dispatch_t dispatch[256];
1545 +
1546 +int
1547 +male_indirect_jump (long offset)
1548 +{
1549 +  dispatch[offset](offset);
1550 +  return 0;
1551 +}
1552 +
1553 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1554 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1555 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1556 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1557 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
1558 +/* { dg-final { scan-assembler {\tpause} } } */
1559 +/* { dg-final { scan-assembler {\tlfence} } } */
1560 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
1561 new file mode 100644
1562 index 00000000000..a25b20dd808
1563 --- /dev/null
1564 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
1565 @@ -0,0 +1,17 @@
1566 +/* { dg-do compile { target *-*-linux* } } */
1567 +/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
1568 +
1569 +extern void bar (void);
1570 +
1571 +void
1572 +foo (void)
1573 +{
1574 +  bar ();
1575 +}
1576 +
1577 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
1578 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
1579 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1580 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
1581 +/* { dg-final { scan-assembler {\tpause} } } */
1582 +/* { dg-final { scan-assembler {\tlfence} } } */
1583 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
1584 new file mode 100644
1585 index 00000000000..cff114a6c29
1586 --- /dev/null
1587 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
1588 @@ -0,0 +1,18 @@
1589 +/* { dg-do compile { target *-*-linux* } } */
1590 +/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
1591 +
1592 +extern void bar (void);
1593 +
1594 +int
1595 +foo (void)
1596 +{
1597 +  bar ();
1598 +  return 0;
1599 +}
1600 +
1601 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
1602 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
1603 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
1604 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
1605 +/* { dg-final { scan-assembler {\tpause} } } */
1606 +/* { dg-final { scan-assembler {\tlfence} } } */
1607 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
1608 new file mode 100644
1609 index 00000000000..afdb6007986
1610 --- /dev/null
1611 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
1612 @@ -0,0 +1,44 @@
1613 +/* { dg-do compile } */
1614 +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
1615 +
1616 +void func0 (void);
1617 +void func1 (void);
1618 +void func2 (void);
1619 +void func3 (void);
1620 +void func4 (void);
1621 +void func4 (void);
1622 +void func5 (void);
1623 +
1624 +void
1625 +bar (int i)
1626 +{
1627 +  switch (i)
1628 +    {
1629 +    default:
1630 +      func0 ();
1631 +      break;
1632 +    case 1:
1633 +      func1 ();
1634 +      break;
1635 +    case 2:
1636 +      func2 ();
1637 +      break;
1638 +    case 3:
1639 +      func3 ();
1640 +      break;
1641 +    case 4:
1642 +      func4 ();
1643 +      break;
1644 +    case 5:
1645 +      func5 ();
1646 +      break;
1647 +    }
1648 +}
1649 +
1650 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
1651 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1652 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1653 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1654 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
1655 +/* { dg-final { scan-assembler {\tpause} } } */
1656 +/* { dg-final { scan-assembler {\tlfence} } } */
1657 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
1658 new file mode 100644
1659 index 00000000000..d64d978b699
1660 --- /dev/null
1661 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
1662 @@ -0,0 +1,23 @@
1663 +/* { dg-do compile } */
1664 +/* { dg-options "-O2 -fno-pic" } */
1665 +
1666 +typedef void (*dispatch_t)(long offset);
1667 +
1668 +dispatch_t dispatch;
1669 +
1670 +extern void male_indirect_jump (long)
1671 +  __attribute__ ((indirect_branch("thunk")));
1672 +
1673 +void
1674 +male_indirect_jump (long offset)
1675 +{
1676 +  dispatch(offset);
1677 +}
1678 +
1679 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1680 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1681 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1682 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1683 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
1684 +/* { dg-final { scan-assembler {\tpause} } } */
1685 +/* { dg-final { scan-assembler {\tlfence} } } */
1686 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
1687 new file mode 100644
1688 index 00000000000..93067454d3d
1689 --- /dev/null
1690 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
1691 @@ -0,0 +1,21 @@
1692 +/* { dg-do compile } */
1693 +/* { dg-options "-O2 -fno-pic" } */
1694 +
1695 +typedef void (*dispatch_t)(long offset);
1696 +
1697 +dispatch_t dispatch[256];
1698 +
1699 +__attribute__ ((indirect_branch("thunk")))
1700 +void
1701 +male_indirect_jump (long offset)
1702 +{
1703 +  dispatch[offset](offset);
1704 +}
1705 +
1706 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1707 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1708 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1709 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1710 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
1711 +/* { dg-final { scan-assembler {\tpause} } } */
1712 +/* { dg-final { scan-assembler {\tlfence} } } */
1713 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
1714 new file mode 100644
1715 index 00000000000..97744d65729
1716 --- /dev/null
1717 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
1718 @@ -0,0 +1,23 @@
1719 +/* { dg-do compile } */
1720 +/* { dg-options "-O2 -fno-pic" } */
1721 +
1722 +typedef void (*dispatch_t)(long offset);
1723 +
1724 +dispatch_t dispatch;
1725 +extern int male_indirect_jump (long)
1726 +  __attribute__ ((indirect_branch("thunk-inline")));
1727 +
1728 +int
1729 +male_indirect_jump (long offset)
1730 +{
1731 +  dispatch(offset);
1732 +  return 0;
1733 +}
1734 +
1735 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1736 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
1737 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
1738 +/* { dg-final { scan-assembler {\tpause} } } */
1739 +/* { dg-final { scan-assembler {\tlfence} } } */
1740 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
1741 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
1742 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
1743 new file mode 100644
1744 index 00000000000..bfce3ea5cb2
1745 --- /dev/null
1746 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
1747 @@ -0,0 +1,22 @@
1748 +/* { dg-do compile } */
1749 +/* { dg-options "-O2 -fno-pic" } */
1750 +
1751 +typedef void (*dispatch_t)(long offset);
1752 +
1753 +dispatch_t dispatch[256];
1754 +
1755 +__attribute__ ((indirect_branch("thunk-inline")))
1756 +int
1757 +male_indirect_jump (long offset)
1758 +{
1759 +  dispatch[offset](offset);
1760 +  return 0;
1761 +}
1762 +
1763 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1764 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
1765 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
1766 +/* { dg-final { scan-assembler {\tpause} } } */
1767 +/* { dg-final { scan-assembler {\tlfence} } } */
1768 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
1769 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
1770 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
1771 new file mode 100644
1772 index 00000000000..0833606046b
1773 --- /dev/null
1774 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
1775 @@ -0,0 +1,22 @@
1776 +/* { dg-do compile } */
1777 +/* { dg-options "-O2 -fno-pic" } */
1778 +
1779 +typedef void (*dispatch_t)(long offset);
1780 +
1781 +dispatch_t dispatch;
1782 +extern int male_indirect_jump (long)
1783 +  __attribute__ ((indirect_branch("thunk-extern")));
1784 +
1785 +int
1786 +male_indirect_jump (long offset)
1787 +{
1788 +  dispatch(offset);
1789 +  return 0;
1790 +}
1791 +
1792 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1793 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
1794 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
1795 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1796 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1797 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
1798 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
1799 new file mode 100644
1800 index 00000000000..2eba0fbd9b2
1801 --- /dev/null
1802 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
1803 @@ -0,0 +1,21 @@
1804 +/* { dg-do compile } */
1805 +/* { dg-options "-O2 -fno-pic" } */
1806 +
1807 +typedef void (*dispatch_t)(long offset);
1808 +
1809 +dispatch_t dispatch[256];
1810 +
1811 +__attribute__ ((indirect_branch("thunk-extern")))
1812 +int
1813 +male_indirect_jump (long offset)
1814 +{
1815 +  dispatch[offset](offset);
1816 +  return 0;
1817 +}
1818 +
1819 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1820 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
1821 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
1822 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
1823 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1824 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
1825 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
1826 new file mode 100644
1827 index 00000000000..f58427eae11
1828 --- /dev/null
1829 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
1830 @@ -0,0 +1,44 @@
1831 +/* { dg-do compile } */
1832 +/* { dg-options "-O2 -fno-pic" } */
1833 +
1834 +void func0 (void);
1835 +void func1 (void);
1836 +void func2 (void);
1837 +void func3 (void);
1838 +void func4 (void);
1839 +void func4 (void);
1840 +void func5 (void);
1841 +
1842 +__attribute__ ((indirect_branch("thunk-extern")))
1843 +void
1844 +bar (int i)
1845 +{
1846 +  switch (i)
1847 +    {
1848 +    default:
1849 +      func0 ();
1850 +      break;
1851 +    case 1:
1852 +      func1 ();
1853 +      break;
1854 +    case 2:
1855 +      func2 ();
1856 +      break;
1857 +    case 3:
1858 +      func3 ();
1859 +      break;
1860 +    case 4:
1861 +      func4 ();
1862 +      break;
1863 +    case 5:
1864 +      func5 ();
1865 +      break;
1866 +    }
1867 +}
1868 +
1869 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
1870 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
1871 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
1872 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
1873 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
1874 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
1875 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
1876 new file mode 100644
1877 index 00000000000..564ed39547c
1878 --- /dev/null
1879 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
1880 @@ -0,0 +1,42 @@
1881 +/* { dg-do compile } */
1882 +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
1883 +
1884 +void func0 (void);
1885 +void func1 (void);
1886 +void func2 (void);
1887 +void func3 (void);
1888 +void func4 (void);
1889 +void func4 (void);
1890 +void func5 (void);
1891 +
1892 +__attribute__ ((indirect_branch("keep")))
1893 +void
1894 +bar (int i)
1895 +{
1896 +  switch (i)
1897 +    {
1898 +    default:
1899 +      func0 ();
1900 +      break;
1901 +    case 1:
1902 +      func1 ();
1903 +      break;
1904 +    case 2:
1905 +      func2 ();
1906 +      break;
1907 +    case 3:
1908 +      func3 ();
1909 +      break;
1910 +    case 4:
1911 +      func4 ();
1912 +      break;
1913 +    case 5:
1914 +      func5 ();
1915 +      break;
1916 +    }
1917 +}
1918 +
1919 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
1920 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
1921 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
1922 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
1923 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
1924 new file mode 100644
1925 index 00000000000..50fbee20a5a
1926 --- /dev/null
1927 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
1928 @@ -0,0 +1,20 @@
1929 +/* { dg-do compile { target { ! x32 } } } */
1930 +/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
1931 +
1932 +void (*dispatch) (char *);
1933 +char buf[10];
1934 +
1935 +void
1936 +foo (void)
1937 +{
1938 +  dispatch (buf);
1939 +}
1940 +
1941 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1942 +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */
1943 +/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
1944 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1945 +/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
1946 +/* { dg-final { scan-assembler "bnd ret" } } */
1947 +/* { dg-final { scan-assembler {\tpause} } } */
1948 +/* { dg-final { scan-assembler {\tlfence} } } */
1949 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
1950 new file mode 100644
1951 index 00000000000..2976e67adce
1952 --- /dev/null
1953 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
1954 @@ -0,0 +1,21 @@
1955 +/* { dg-do compile { target { ! x32 } } } */
1956 +/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
1957 +
1958 +void (*dispatch) (char *);
1959 +char buf[10];
1960 +
1961 +int
1962 +foo (void)
1963 +{
1964 +  dispatch (buf);
1965 +  return 0;
1966 +}
1967 +
1968 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
1969 +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */
1970 +/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
1971 +/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
1972 +/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
1973 +/* { dg-final { scan-assembler "bnd ret" } } */
1974 +/* { dg-final { scan-assembler {\tpause} } } */
1975 +/* { dg-final { scan-assembler {\tlfence} } } */
1976 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
1977 new file mode 100644
1978 index 00000000000..da4bc98ef23
1979 --- /dev/null
1980 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
1981 @@ -0,0 +1,19 @@
1982 +/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
1983 +/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
1984 +
1985 +void bar (char *);
1986 +char buf[10];
1987 +
1988 +void
1989 +foo (void)
1990 +{
1991 +  bar (buf);
1992 +}
1993 +
1994 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
1995 +/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
1996 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
1997 +/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
1998 +/* { dg-final { scan-assembler "bnd ret" } } */
1999 +/* { dg-final { scan-assembler {\tpause} } } */
2000 +/* { dg-final { scan-assembler {\tlfence} } } */
2001 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
2002 new file mode 100644
2003 index 00000000000..c64d12ef989
2004 --- /dev/null
2005 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
2006 @@ -0,0 +1,20 @@
2007 +/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
2008 +/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
2009 +
2010 +void bar (char *);
2011 +char buf[10];
2012 +
2013 +int
2014 +foo (void)
2015 +{
2016 +  bar (buf);
2017 +  return 0;
2018 +}
2019 +
2020 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
2021 +/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk" } } */
2022 +/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
2023 +/* { dg-final { scan-assembler-times "bnd call\[ \t\]*\.LIND" 2 } } */
2024 +/* { dg-final { scan-assembler "bnd ret" } } */
2025 +/* { dg-final { scan-assembler {\tpause} } } */
2026 +/* { dg-final { scan-assembler {\tlfence} } } */
2027 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
2028 new file mode 100644
2029 index 00000000000..49f27b49465
2030 --- /dev/null
2031 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
2032 @@ -0,0 +1,19 @@
2033 +/* { dg-do compile } */
2034 +/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
2035 +
2036 +typedef void (*dispatch_t)(long offset);
2037 +
2038 +dispatch_t dispatch;
2039 +
2040 +void
2041 +male_indirect_jump (long offset)
2042 +{
2043 +  dispatch(offset);
2044 +}
2045 +
2046 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
2047 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
2048 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
2049 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
2050 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
2051 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
2052 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
2053 new file mode 100644
2054 index 00000000000..a1e3eb6fc74
2055 --- /dev/null
2056 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
2057 @@ -0,0 +1,19 @@
2058 +/* { dg-do compile } */
2059 +/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
2060 +
2061 +typedef void (*dispatch_t)(long offset);
2062 +
2063 +dispatch_t dispatch[256];
2064 +
2065 +void
2066 +male_indirect_jump (long offset)
2067 +{
2068 +  dispatch[offset](offset);
2069 +}
2070 +
2071 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
2072 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
2073 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
2074 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
2075 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
2076 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
2077 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
2078 new file mode 100644
2079 index 00000000000..395634e7e5c
2080 --- /dev/null
2081 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
2082 @@ -0,0 +1,20 @@
2083 +/* { dg-do compile } */
2084 +/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
2085 +
2086 +typedef void (*dispatch_t)(long offset);
2087 +
2088 +dispatch_t dispatch;
2089 +
2090 +int
2091 +male_indirect_jump (long offset)
2092 +{
2093 +  dispatch(offset);
2094 +  return 0;
2095 +}
2096 +
2097 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
2098 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
2099 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
2100 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
2101 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
2102 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
2103 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
2104 new file mode 100644
2105 index 00000000000..fd3f63379a1
2106 --- /dev/null
2107 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
2108 @@ -0,0 +1,20 @@
2109 +/* { dg-do compile } */
2110 +/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
2111 +
2112 +typedef void (*dispatch_t)(long offset);
2113 +
2114 +dispatch_t dispatch[256];
2115 +
2116 +int
2117 +male_indirect_jump (long offset)
2118 +{
2119 +  dispatch[offset](offset);
2120 +  return 0;
2121 +}
2122 +
2123 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
2124 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
2125 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
2126 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
2127 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
2128 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
2129 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
2130 new file mode 100644
2131 index 00000000000..ba2f92b6f34
2132 --- /dev/null
2133 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
2134 @@ -0,0 +1,16 @@
2135 +/* { dg-do compile { target *-*-linux* } } */
2136 +/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
2137 +
2138 +extern void bar (void);
2139 +
2140 +void
2141 +foo (void)
2142 +{
2143 +  bar ();
2144 +}
2145 +
2146 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
2147 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
2148 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
2149 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
2150 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
2151 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
2152 new file mode 100644
2153 index 00000000000..0c5a2d472c6
2154 --- /dev/null
2155 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
2156 @@ -0,0 +1,17 @@
2157 +/* { dg-do compile { target *-*-linux* } } */
2158 +/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
2159 +
2160 +extern void bar (void);
2161 +
2162 +int
2163 +foo (void)
2164 +{
2165 +  bar ();
2166 +  return 0;
2167 +}
2168 +
2169 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
2170 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */
2171 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */
2172 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
2173 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
2174 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
2175 new file mode 100644
2176 index 00000000000..665252327aa
2177 --- /dev/null
2178 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
2179 @@ -0,0 +1,43 @@
2180 +/* { dg-do compile } */
2181 +/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
2182 +
2183 +void func0 (void);
2184 +void func1 (void);
2185 +void func2 (void);
2186 +void func3 (void);
2187 +void func4 (void);
2188 +void func4 (void);
2189 +void func5 (void);
2190 +
2191 +void
2192 +bar (int i)
2193 +{
2194 +  switch (i)
2195 +    {
2196 +    default:
2197 +      func0 ();
2198 +      break;
2199 +    case 1:
2200 +      func1 ();
2201 +      break;
2202 +    case 2:
2203 +      func2 ();
2204 +      break;
2205 +    case 3:
2206 +      func3 ();
2207 +      break;
2208 +    case 4:
2209 +      func4 ();
2210 +      break;
2211 +    case 5:
2212 +      func5 ();
2213 +      break;
2214 +    }
2215 +}
2216 +
2217 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
2218 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
2219 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
2220 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
2221 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
2222 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
2223 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
2224 new file mode 100644
2225 index 00000000000..68c0ff713b3
2226 --- /dev/null
2227 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
2228 @@ -0,0 +1,20 @@
2229 +/* { dg-do compile } */
2230 +/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
2231 +
2232 +typedef void (*dispatch_t)(long offset);
2233 +
2234 +dispatch_t dispatch;
2235 +
2236 +void
2237 +male_indirect_jump (long offset)
2238 +{
2239 +  dispatch(offset);
2240 +}
2241 +
2242 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
2243 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
2244 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
2245 +/* { dg-final { scan-assembler {\tpause} } } */
2246 +/* { dg-final { scan-assembler {\tlfence} } } */
2247 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
2248 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
2249 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
2250 new file mode 100644
2251 index 00000000000..e2da1fcb683
2252 --- /dev/null
2253 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
2254 @@ -0,0 +1,20 @@
2255 +/* { dg-do compile } */
2256 +/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
2257 +
2258 +typedef void (*dispatch_t)(long offset);
2259 +
2260 +dispatch_t dispatch[256];
2261 +
2262 +void
2263 +male_indirect_jump (long offset)
2264 +{
2265 +  dispatch[offset](offset);
2266 +}
2267 +
2268 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
2269 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
2270 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
2271 +/* { dg-final { scan-assembler {\tpause} } } */
2272 +/* { dg-final { scan-assembler {\tlfence} } } */
2273 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
2274 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
2275 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
2276 new file mode 100644
2277 index 00000000000..244fec708d6
2278 --- /dev/null
2279 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
2280 @@ -0,0 +1,21 @@
2281 +/* { dg-do compile } */
2282 +/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
2283 +
2284 +typedef void (*dispatch_t)(long offset);
2285 +
2286 +dispatch_t dispatch;
2287 +
2288 +int
2289 +male_indirect_jump (long offset)
2290 +{
2291 +  dispatch(offset);
2292 +  return 0;
2293 +}
2294 +
2295 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
2296 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
2297 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
2298 +/* { dg-final { scan-assembler-times {\tpause} 1 } } */
2299 +/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
2300 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
2301 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
2302 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
2303 new file mode 100644
2304 index 00000000000..107ebe32f54
2305 --- /dev/null
2306 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
2307 @@ -0,0 +1,21 @@
2308 +/* { dg-do compile } */
2309 +/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
2310 +
2311 +typedef void (*dispatch_t)(long offset);
2312 +
2313 +dispatch_t dispatch[256];
2314 +
2315 +int
2316 +male_indirect_jump (long offset)
2317 +{
2318 +  dispatch[offset](offset);
2319 +  return 0;
2320 +}
2321 +
2322 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
2323 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
2324 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
2325 +/* { dg-final { scan-assembler-times {\tpause} 1 } } */
2326 +/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
2327 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
2328 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
2329 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
2330 new file mode 100644
2331 index 00000000000..17b04ef2229
2332 --- /dev/null
2333 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
2334 @@ -0,0 +1,17 @@
2335 +/* { dg-do compile { target *-*-linux* } } */
2336 +/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
2337 +
2338 +extern void bar (void);
2339 +
2340 +void
2341 +foo (void)
2342 +{
2343 +  bar ();
2344 +}
2345 +
2346 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
2347 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
2348 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
2349 +/* { dg-final { scan-assembler {\tpause} } } */
2350 +/* { dg-final { scan-assembler {\tlfence} } } */
2351 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
2352 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
2353 new file mode 100644
2354 index 00000000000..d9eb11285aa
2355 --- /dev/null
2356 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
2357 @@ -0,0 +1,18 @@
2358 +/* { dg-do compile { target *-*-linux* } } */
2359 +/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
2360 +
2361 +extern void bar (void);
2362 +
2363 +int
2364 +foo (void)
2365 +{
2366 +  bar ();
2367 +  return 0;
2368 +}
2369 +
2370 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
2371 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
2372 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
2373 +/* { dg-final { scan-assembler-times {\tpause} 1 } } */
2374 +/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
2375 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
2376 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
2377 new file mode 100644
2378 index 00000000000..d02b1dcb1b9
2379 --- /dev/null
2380 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
2381 @@ -0,0 +1,44 @@
2382 +/* { dg-do compile } */
2383 +/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
2384 +
2385 +void func0 (void);
2386 +void func1 (void);
2387 +void func2 (void);
2388 +void func3 (void);
2389 +void func4 (void);
2390 +void func4 (void);
2391 +void func5 (void);
2392 +
2393 +void
2394 +bar (int i)
2395 +{
2396 +  switch (i)
2397 +    {
2398 +    default:
2399 +      func0 ();
2400 +      break;
2401 +    case 1:
2402 +      func1 ();
2403 +      break;
2404 +    case 2:
2405 +      func2 ();
2406 +      break;
2407 +    case 3:
2408 +      func3 ();
2409 +      break;
2410 +    case 4:
2411 +      func4 ();
2412 +      break;
2413 +    case 5:
2414 +      func5 ();
2415 +      break;
2416 +    }
2417 +}
2418 +
2419 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
2420 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
2421 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
2422 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
2423 +/* { dg-final { scan-assembler {\tpause} } } */
2424 +/* { dg-final { scan-assembler {\tlfence} } } */
2425 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
2426
2427 commit ad272ea92bdadd7f94bc1dafb35529959d3de1f0
2428 Author: H.J. Lu <hjl.tools@gmail.com>
2429 Date:   Sat Jan 6 22:29:56 2018 -0800
2430
2431     x86: Add -mfunction-return=
2432     
2433     Add -mfunction-return= option to convert function return to call and
2434     return thunks.  The default is 'keep', which keeps function return
2435     unmodified.  'thunk' converts function return to call and return thunk.
2436     'thunk-inline' converts function return to inlined call and return thunk.
2437     'thunk-extern' converts function return to external call and return
2438     thunk provided in a separate object file.  You can control this behavior
2439     for a specific function by using the function attribute function_return.
2440     
2441     Function return thunk is the same as memory thunk for -mindirect-branch=
2442     where the return address is at the top of the stack:
2443     
2444     __x86_return_thunk:
2445             call L2
2446     L1:
2447             pause
2448             lfence
2449             jmp L1
2450     L2:
2451             lea 8(%rsp), %rsp|lea 4(%esp), %esp
2452             ret
2453     
2454     and function return becomes
2455     
2456             jmp __x86_return_thunk
2457     
2458     -mindirect-branch= tests are updated with -mfunction-return=keep to
2459     avoid false test failures when -mfunction-return=thunk is added to
2460     RUNTESTFLAGS for "make check".
2461     
2462     gcc/
2463     
2464             * config/i386/i386-protos.h (ix86_output_function_return): New.
2465             * config/i386/i386.c (ix86_set_indirect_branch_type): Also
2466             set function_return_type.
2467             (indirect_thunk_name): Add ret_p to indicate thunk for function
2468             return.
2469             (output_indirect_thunk_function): Pass false to
2470             indirect_thunk_name.
2471             (ix86_output_indirect_branch): Likewise.
2472             (output_indirect_thunk_function): Create alias for function
2473             return thunk if regno < 0.
2474             (ix86_output_function_return): New function.
2475             (ix86_handle_fndecl_attribute): Handle function_return.
2476             (ix86_attribute_table): Add function_return.
2477             * config/i386/i386.h (machine_function): Add
2478             function_return_type.
2479             * config/i386/i386.md (simple_return_internal): Use
2480             ix86_output_function_return.
2481             (simple_return_internal_long): Likewise.
2482             * config/i386/i386.opt (mfunction-return=): New option.
2483             (indirect_branch): Mention -mfunction-return=.
2484             * doc/extend.texi: Document function_return function attribute.
2485             * doc/invoke.texi: Document -mfunction-return= option.
2486     
2487     gcc/testsuite/
2488     
2489             * gcc.target/i386/indirect-thunk-1.c (dg-options): Add
2490             -mfunction-return=keep.
2491             * gcc.target/i386/indirect-thunk-2.c: Likewise.
2492             * gcc.target/i386/indirect-thunk-3.c: Likewise.
2493             * gcc.target/i386/indirect-thunk-4.c: Likewise.
2494             * gcc.target/i386/indirect-thunk-5.c: Likewise.
2495             * gcc.target/i386/indirect-thunk-6.c: Likewise.
2496             * gcc.target/i386/indirect-thunk-7.c: Likewise.
2497             * gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
2498             * gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
2499             * gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
2500             * gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
2501             * gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
2502             * gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
2503             * gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
2504             * gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
2505             * gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
2506             * gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
2507             * gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
2508             * gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
2509             * gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
2510             * gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
2511             * gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
2512             * gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
2513             * gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
2514             * gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
2515             * gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
2516             * gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
2517             * gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
2518             * gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
2519             * gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
2520             * gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
2521             * gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
2522             * gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
2523             * gcc.target/i386/ret-thunk-1.c: New test.
2524             * gcc.target/i386/ret-thunk-10.c: Likewise.
2525             * gcc.target/i386/ret-thunk-11.c: Likewise.
2526             * gcc.target/i386/ret-thunk-12.c: Likewise.
2527             * gcc.target/i386/ret-thunk-13.c: Likewise.
2528             * gcc.target/i386/ret-thunk-14.c: Likewise.
2529             * gcc.target/i386/ret-thunk-15.c: Likewise.
2530             * gcc.target/i386/ret-thunk-16.c: Likewise.
2531             * gcc.target/i386/ret-thunk-2.c: Likewise.
2532             * gcc.target/i386/ret-thunk-3.c: Likewise.
2533             * gcc.target/i386/ret-thunk-4.c: Likewise.
2534             * gcc.target/i386/ret-thunk-5.c: Likewise.
2535             * gcc.target/i386/ret-thunk-6.c: Likewise.
2536             * gcc.target/i386/ret-thunk-7.c: Likewise.
2537             * gcc.target/i386/ret-thunk-8.c: Likewise.
2538             * gcc.target/i386/ret-thunk-9.c: Likewise.
2539
2540 diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
2541 index bcdd9872db9..42eece35766 100644
2542 --- a/gcc/config/i386/i386-protos.h
2543 +++ b/gcc/config/i386/i386-protos.h
2544 @@ -314,6 +314,7 @@ extern enum attr_cpu ix86_schedule;
2545  
2546  extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
2547  extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
2548 +extern const char * ix86_output_function_return (bool long_p);
2549  extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
2550                                                 enum machine_mode mode);
2551  
2552 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
2553 index f1c58faa035..4bfe2fa8c1d 100644
2554 --- a/gcc/config/i386/i386.c
2555 +++ b/gcc/config/i386/i386.c
2556 @@ -7188,6 +7188,31 @@ ix86_set_indirect_branch_type (tree fndecl)
2557        else
2558         cfun->machine->indirect_branch_type = ix86_indirect_branch;
2559      }
2560 +
2561 +  if (cfun->machine->function_return_type == indirect_branch_unset)
2562 +    {
2563 +      tree attr = lookup_attribute ("function_return",
2564 +                                   DECL_ATTRIBUTES (fndecl));
2565 +      if (attr != NULL)
2566 +       {
2567 +         tree args = TREE_VALUE (attr);
2568 +         if (args == NULL)
2569 +           gcc_unreachable ();
2570 +         tree cst = TREE_VALUE (args);
2571 +         if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0)
2572 +           cfun->machine->function_return_type = indirect_branch_keep;
2573 +         else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0)
2574 +           cfun->machine->function_return_type = indirect_branch_thunk;
2575 +         else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0)
2576 +           cfun->machine->function_return_type = indirect_branch_thunk_inline;
2577 +         else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0)
2578 +           cfun->machine->function_return_type = indirect_branch_thunk_extern;
2579 +         else
2580 +           gcc_unreachable ();
2581 +       }
2582 +      else
2583 +       cfun->machine->function_return_type = ix86_function_return;
2584 +    }
2585  }
2586  
2587  /* Establish appropriate back-end context for processing the function
2588 @@ -11990,8 +12015,12 @@ static int indirect_thunks_bnd_used;
2589  /* Fills in the label name that should be used for the indirect thunk.  */
2590  
2591  static void
2592 -indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
2593 +indirect_thunk_name (char name[32], int regno, bool need_bnd_p,
2594 +                    bool ret_p)
2595  {
2596 +  if (regno >= 0 && ret_p)
2597 +    gcc_unreachable ();
2598 +
2599    if (USE_HIDDEN_LINKONCE)
2600      {
2601        const char *bnd = need_bnd_p ? "_bnd" : "";
2602 @@ -12006,7 +12035,10 @@ indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
2603                    bnd, reg_prefix, reg_names[regno]);
2604         }
2605        else
2606 -       sprintf (name, "__x86_indirect_thunk%s", bnd);
2607 +       {
2608 +         const char *ret = ret_p ? "return" : "indirect";
2609 +         sprintf (name, "__x86_%s_thunk%s", ret, bnd);
2610 +       }
2611      }
2612    else
2613      {
2614 @@ -12019,10 +12051,20 @@ indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
2615         }
2616        else
2617         {
2618 -         if (need_bnd_p)
2619 -           ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
2620 +         if (ret_p)
2621 +           {
2622 +             if (need_bnd_p)
2623 +               ASM_GENERATE_INTERNAL_LABEL (name, "LRTB", 0);
2624 +             else
2625 +               ASM_GENERATE_INTERNAL_LABEL (name, "LRT", 0);
2626 +           }
2627           else
2628 -           ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
2629 +           {
2630 +             if (need_bnd_p)
2631 +               ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
2632 +             else
2633 +               ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
2634 +           }
2635         }
2636      }
2637  }
2638 @@ -12117,7 +12159,7 @@ output_indirect_thunk_function (bool need_bnd_p, int regno)
2639    tree decl;
2640  
2641    /* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd.  */
2642 -  indirect_thunk_name (name, regno, need_bnd_p);
2643 +  indirect_thunk_name (name, regno, need_bnd_p, false);
2644    decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
2645                      get_identifier (name),
2646                      build_function_type_list (void_type_node, NULL_TREE));
2647 @@ -12160,6 +12202,35 @@ output_indirect_thunk_function (bool need_bnd_p, int regno)
2648         ASM_OUTPUT_LABEL (asm_out_file, name);
2649        }
2650  
2651 +  if (regno < 0)
2652 +    {
2653 +      /* Create alias for __x86.return_thunk/__x86.return_thunk_bnd.  */
2654 +      char alias[32];
2655 +
2656 +      indirect_thunk_name (alias, regno, need_bnd_p, true);
2657 +      ASM_OUTPUT_DEF (asm_out_file, alias, name);
2658 +#if TARGET_MACHO
2659 +      if (TARGET_MACHO)
2660 +       {
2661 +         fputs ("\t.weak_definition\t", asm_out_file);
2662 +         assemble_name (asm_out_file, alias);
2663 +         fputs ("\n\t.private_extern\t", asm_out_file);
2664 +         assemble_name (asm_out_file, alias);
2665 +         putc ('\n', asm_out_file);
2666 +       }
2667 +#else
2668 +      if (USE_HIDDEN_LINKONCE)
2669 +       {
2670 +         fputs ("\t.globl\t", asm_out_file);
2671 +         assemble_name (asm_out_file, alias);
2672 +         putc ('\n', asm_out_file);
2673 +         fputs ("\t.hidden\t", asm_out_file);
2674 +         assemble_name (asm_out_file, alias);
2675 +         putc ('\n', asm_out_file);
2676 +       }
2677 +#endif
2678 +    }
2679 +
2680    DECL_INITIAL (decl) = make_node (BLOCK);
2681    current_function_decl = decl;
2682    allocate_struct_function (decl, false);
2683 @@ -28760,7 +28831,7 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
2684           else
2685             indirect_thunks_used |= 1 << i;
2686         }
2687 -      indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
2688 +      indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false);
2689        thunk_name = thunk_name_buf;
2690      }
2691    else
2692 @@ -28869,7 +28940,7 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
2693           else
2694             indirect_thunk_needed = true;
2695         }
2696 -      indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
2697 +      indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false);
2698        thunk_name = thunk_name_buf;
2699      }
2700    else
2701 @@ -29004,6 +29075,46 @@ ix86_output_indirect_jmp (rtx call_op, bool ret_p)
2702      return "%!jmp\t%A0";
2703  }
2704  
2705 +/* Output function return.  CALL_OP is the jump target.  Add a REP
2706 +   prefix to RET if LONG_P is true and function return is kept.  */
2707 +
2708 +const char *
2709 +ix86_output_function_return (bool long_p)
2710 +{
2711 +  if (cfun->machine->function_return_type != indirect_branch_keep)
2712 +    {
2713 +      char thunk_name[32];
2714 +      bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
2715 +
2716 +      if (cfun->machine->function_return_type
2717 +         != indirect_branch_thunk_inline)
2718 +       {
2719 +         bool need_thunk = (cfun->machine->function_return_type
2720 +                            == indirect_branch_thunk);
2721 +         indirect_thunk_name (thunk_name, -1, need_bnd_p, true);
2722 +         if (need_bnd_p)
2723 +           {
2724 +             indirect_thunk_bnd_needed |= need_thunk;
2725 +             fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
2726 +           }
2727 +         else
2728 +           {
2729 +             indirect_thunk_needed |= need_thunk;
2730 +             fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
2731 +           }
2732 +       }
2733 +      else
2734 +       output_indirect_thunk (need_bnd_p, -1);
2735 +
2736 +      return "";
2737 +    }
2738 +
2739 +  if (!long_p || ix86_bnd_prefixed_insn_p (current_output_insn))
2740 +    return "%!ret";
2741 +
2742 +  return "rep%; ret";
2743 +}
2744 +
2745  /* Output the assembly for a call instruction.  */
2746  
2747  const char *
2748 @@ -42075,6 +42186,28 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
2749         }
2750      }
2751  
2752 +  if (is_attribute_p ("function_return", name))
2753 +    {
2754 +      tree cst = TREE_VALUE (args);
2755 +      if (TREE_CODE (cst) != STRING_CST)
2756 +       {
2757 +         warning (OPT_Wattributes,
2758 +                  "%qE attribute requires a string constant argument",
2759 +                  name);
2760 +         *no_add_attrs = true;
2761 +       }
2762 +      else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
2763 +              && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
2764 +              && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
2765 +              && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
2766 +       {
2767 +         warning (OPT_Wattributes,
2768 +                  "argument to %qE attribute is not "
2769 +                  "(keep|thunk|thunk-inline|thunk-extern)", name);
2770 +         *no_add_attrs = true;
2771 +       }
2772 +    }
2773 +
2774    return NULL_TREE;
2775  }
2776  
2777 @@ -46385,6 +46518,8 @@ static const struct attribute_spec ix86_attribute_table[] =
2778      ix86_handle_no_caller_saved_registers_attribute, false },
2779    { "indirect_branch", 1, 1, true, false, false,
2780      ix86_handle_fndecl_attribute, false },
2781 +  { "function_return", 1, 1, true, false, false,
2782 +    ix86_handle_fndecl_attribute, false },
2783  
2784    /* End element.  */
2785    { NULL,        0, 0, false, false, false, NULL, false }
2786 diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
2787 index 9d2209e605b..45593068905 100644
2788 --- a/gcc/config/i386/i386.h
2789 +++ b/gcc/config/i386/i386.h
2790 @@ -2616,6 +2616,9 @@ struct GTY(()) machine_function {
2791       "indirect_jump" or "tablejump".  */
2792    BOOL_BITFIELD has_local_indirect_jump : 1;
2793  
2794 +  /* How to generate function return.  */
2795 +  ENUM_BITFIELD(indirect_branch) function_return_type : 3;
2796 +
2797    /* If true, the current function is a function specified with
2798       the "interrupt" or "no_caller_saved_registers" attribute.  */
2799    BOOL_BITFIELD no_caller_saved_registers : 1;
2800 diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
2801 index e32f2311065..3ac79ff6ee6 100644
2802 --- a/gcc/config/i386/i386.md
2803 +++ b/gcc/config/i386/i386.md
2804 @@ -12313,7 +12313,7 @@
2805  (define_insn "simple_return_internal"
2806    [(simple_return)]
2807    "reload_completed"
2808 -  "%!ret"
2809 +  "* return ix86_output_function_return (false);"
2810    [(set_attr "length" "1")
2811     (set_attr "atom_unit" "jeu")
2812     (set_attr "length_immediate" "0")
2813 @@ -12335,12 +12335,7 @@
2814    [(simple_return)
2815     (unspec [(const_int 0)] UNSPEC_REP)]
2816    "reload_completed"
2817 -{
2818 -  if (ix86_bnd_prefixed_insn_p (insn))
2819 -    return "%!ret";
2820 -
2821 -  return "rep%; ret";
2822 -}
2823 +  "* return ix86_output_function_return (true);"
2824    [(set_attr "length" "2")
2825     (set_attr "atom_unit" "jeu")
2826     (set_attr "length_immediate" "0")
2827 diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
2828 index c076d9c70ab..b07388d95a9 100644
2829 --- a/gcc/config/i386/i386.opt
2830 +++ b/gcc/config/i386/i386.opt
2831 @@ -932,9 +932,13 @@ mindirect-branch=
2832  Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep)
2833  Convert indirect call and jump to call and return thunks.
2834  
2835 +mfunction-return=
2836 +Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_function_return) Init(indirect_branch_keep)
2837 +Convert function return to call and return thunk.
2838 +
2839  Enum
2840  Name(indirect_branch) Type(enum indirect_branch)
2841 -Known indirect branch choices (for use with the -mindirect-branch= option):
2842 +Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options):
2843  
2844  EnumValue
2845  Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
2846 diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
2847 index 935381da6fa..46e0a3623a6 100644
2848 --- a/gcc/doc/extend.texi
2849 +++ b/gcc/doc/extend.texi
2850 @@ -5550,6 +5550,15 @@ call and jump to call and return thunk.  @samp{thunk-inline} converts
2851  indirect call and jump to inlined call and return thunk.
2852  @samp{thunk-extern} converts indirect call and jump to external call
2853  and return thunk provided in a separate object file.
2854 +
2855 +@item function_return("@var{choice}")
2856 +@cindex @code{function_return} function attribute, x86
2857 +On x86 targets, the @code{function_return} attribute causes the compiler
2858 +to convert function return with @var{choice}.  @samp{keep} keeps function
2859 +return unmodified.  @samp{thunk} converts function return to call and
2860 +return thunk.  @samp{thunk-inline} converts function return to inlined
2861 +call and return thunk.  @samp{thunk-extern} converts function return to
2862 +external call and return thunk provided in a separate object file.
2863  @end table
2864  
2865  On the x86, the inliner does not inline a
2866 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
2867 index 4979c8c939d..f3eb54b1668 100644
2868 --- a/gcc/doc/invoke.texi
2869 +++ b/gcc/doc/invoke.texi
2870 @@ -1211,7 +1211,7 @@ See RS/6000 and PowerPC Options.
2871  -mavx256-split-unaligned-load  -mavx256-split-unaligned-store @gol
2872  -malign-data=@var{type}  -mstack-protector-guard=@var{guard} @gol
2873  -mmitigate-rop  -mgeneral-regs-only @gol
2874 --mindirect-branch=@var{choice}}
2875 +-mindirect-branch=@var{choice} -mfunction-return==@var{choice}}
2876  
2877  @emph{x86 Windows Options}
2878  @gccoptlist{-mconsole  -mcygwin  -mno-cygwin  -mdll @gol
2879 @@ -25698,6 +25698,17 @@ to external call and return thunk provided in a separate object file.
2880  You can control this behavior for a specific function by using the
2881  function attribute @code{indirect_branch}.  @xref{Function Attributes}.
2882  
2883 +@item -mfunction-return=@var{choice}
2884 +@opindex -mfunction-return
2885 +Convert function return with @var{choice}.  The default is @samp{keep},
2886 +which keeps function return unmodified.  @samp{thunk} converts function
2887 +return to call and return thunk.  @samp{thunk-inline} converts function
2888 +return to inlined call and return thunk.  @samp{thunk-extern} converts
2889 +function return to external call and return thunk provided in a separate
2890 +object file.  You can control this behavior for a specific function by
2891 +using the function attribute @code{function_return}.
2892 +@xref{Function Attributes}.
2893 +
2894  @end table
2895  
2896  These @samp{-m} switches are supported in addition to the above
2897 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
2898 index d983e1c3e26..f076155c91a 100644
2899 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
2900 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
2901 @@ -1,5 +1,5 @@
2902  /* { dg-do compile } */
2903 -/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
2904 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
2905  
2906  typedef void (*dispatch_t)(long offset);
2907  
2908 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
2909 index 58f09b42d8a..d7984f592fe 100644
2910 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
2911 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
2912 @@ -1,5 +1,5 @@
2913  /* { dg-do compile } */
2914 -/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
2915 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
2916  
2917  typedef void (*dispatch_t)(long offset);
2918  
2919 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
2920 index f20d35c19b6..3257d0a2e16 100644
2921 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
2922 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
2923 @@ -1,5 +1,5 @@
2924  /* { dg-do compile } */
2925 -/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
2926 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
2927  
2928  typedef void (*dispatch_t)(long offset);
2929  
2930 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
2931 index 0eff8fb658a..7cab2df6474 100644
2932 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
2933 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
2934 @@ -1,5 +1,5 @@
2935  /* { dg-do compile } */
2936 -/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
2937 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
2938  
2939  typedef void (*dispatch_t)(long offset);
2940  
2941 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
2942 index a25b20dd808..b4836c38d6c 100644
2943 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
2944 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
2945 @@ -1,5 +1,5 @@
2946  /* { dg-do compile { target *-*-linux* } } */
2947 -/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
2948 +/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
2949  
2950  extern void bar (void);
2951  
2952 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
2953 index cff114a6c29..1f06bd1af74 100644
2954 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
2955 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
2956 @@ -1,5 +1,5 @@
2957  /* { dg-do compile { target *-*-linux* } } */
2958 -/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
2959 +/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
2960  
2961  extern void bar (void);
2962  
2963 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
2964 index afdb6007986..0b3fef86a20 100644
2965 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
2966 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
2967 @@ -1,5 +1,5 @@
2968  /* { dg-do compile } */
2969 -/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
2970 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
2971  
2972  void func0 (void);
2973  void func1 (void);
2974 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
2975 index d64d978b699..5f6cfc17b56 100644
2976 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
2977 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
2978 @@ -1,5 +1,5 @@
2979  /* { dg-do compile } */
2980 -/* { dg-options "-O2 -fno-pic" } */
2981 +/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
2982  
2983  typedef void (*dispatch_t)(long offset);
2984  
2985 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
2986 index 93067454d3d..b256160ec80 100644
2987 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
2988 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
2989 @@ -1,5 +1,5 @@
2990  /* { dg-do compile } */
2991 -/* { dg-options "-O2 -fno-pic" } */
2992 +/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
2993  
2994  typedef void (*dispatch_t)(long offset);
2995  
2996 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
2997 index 97744d65729..567c95051d6 100644
2998 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
2999 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
3000 @@ -1,5 +1,5 @@
3001  /* { dg-do compile } */
3002 -/* { dg-options "-O2 -fno-pic" } */
3003 +/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
3004  
3005  typedef void (*dispatch_t)(long offset);
3006  
3007 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
3008 index bfce3ea5cb2..3b662af7d5d 100644
3009 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
3010 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
3011 @@ -1,5 +1,5 @@
3012  /* { dg-do compile } */
3013 -/* { dg-options "-O2 -fno-pic" } */
3014 +/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
3015  
3016  typedef void (*dispatch_t)(long offset);
3017  
3018 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
3019 index 0833606046b..98785a38248 100644
3020 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
3021 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
3022 @@ -1,5 +1,5 @@
3023  /* { dg-do compile } */
3024 -/* { dg-options "-O2 -fno-pic" } */
3025 +/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
3026  
3027  typedef void (*dispatch_t)(long offset);
3028  
3029 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
3030 index 2eba0fbd9b2..a498a39e404 100644
3031 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
3032 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
3033 @@ -1,5 +1,5 @@
3034  /* { dg-do compile } */
3035 -/* { dg-options "-O2 -fno-pic" } */
3036 +/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
3037  
3038  typedef void (*dispatch_t)(long offset);
3039  
3040 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
3041 index f58427eae11..66f295d1eb6 100644
3042 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
3043 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
3044 @@ -1,5 +1,5 @@
3045  /* { dg-do compile } */
3046 -/* { dg-options "-O2 -fno-pic" } */
3047 +/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
3048  
3049  void func0 (void);
3050  void func1 (void);
3051 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
3052 index 564ed39547c..d730d31bda1 100644
3053 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
3054 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
3055 @@ -1,5 +1,5 @@
3056  /* { dg-do compile } */
3057 -/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
3058 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
3059  
3060  void func0 (void);
3061  void func1 (void);
3062 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
3063 index 50fbee20a5a..aacb814d737 100644
3064 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
3065 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
3066 @@ -1,5 +1,5 @@
3067  /* { dg-do compile { target { ! x32 } } } */
3068 -/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
3069 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
3070  
3071  void (*dispatch) (char *);
3072  char buf[10];
3073 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
3074 index 2976e67adce..7b44dda23df 100644
3075 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
3076 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
3077 @@ -1,5 +1,5 @@
3078  /* { dg-do compile { target { ! x32 } } } */
3079 -/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
3080 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
3081  
3082  void (*dispatch) (char *);
3083  char buf[10];
3084 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
3085 index da4bc98ef23..70b4fb36eea 100644
3086 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
3087 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
3088 @@ -1,5 +1,5 @@
3089  /* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
3090 -/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
3091 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
3092  
3093  void bar (char *);
3094  char buf[10];
3095 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
3096 index c64d12ef989..3baf03ee77c 100644
3097 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
3098 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
3099 @@ -1,5 +1,5 @@
3100  /* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
3101 -/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
3102 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
3103  
3104  void bar (char *);
3105  char buf[10];
3106 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
3107 index 49f27b49465..637fc3d3f4e 100644
3108 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
3109 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
3110 @@ -1,5 +1,5 @@
3111  /* { dg-do compile } */
3112 -/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
3113 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
3114  
3115  typedef void (*dispatch_t)(long offset);
3116  
3117 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
3118 index a1e3eb6fc74..ff9efe03fe6 100644
3119 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
3120 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
3121 @@ -1,5 +1,5 @@
3122  /* { dg-do compile } */
3123 -/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
3124 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
3125  
3126  typedef void (*dispatch_t)(long offset);
3127  
3128 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
3129 index 395634e7e5c..2686a5f2db4 100644
3130 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
3131 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
3132 @@ -1,5 +1,5 @@
3133  /* { dg-do compile } */
3134 -/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
3135 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
3136  
3137  typedef void (*dispatch_t)(long offset);
3138  
3139 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
3140 index fd3f63379a1..f07f6b214ad 100644
3141 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
3142 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
3143 @@ -1,5 +1,5 @@
3144  /* { dg-do compile } */
3145 -/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
3146 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
3147  
3148  typedef void (*dispatch_t)(long offset);
3149  
3150 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
3151 index ba2f92b6f34..21740ac5b7f 100644
3152 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
3153 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
3154 @@ -1,5 +1,5 @@
3155  /* { dg-do compile { target *-*-linux* } } */
3156 -/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
3157 +/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
3158  
3159  extern void bar (void);
3160  
3161 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
3162 index 0c5a2d472c6..a77c1f470b8 100644
3163 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
3164 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
3165 @@ -1,5 +1,5 @@
3166  /* { dg-do compile { target *-*-linux* } } */
3167 -/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
3168 +/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
3169  
3170  extern void bar (void);
3171  
3172 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
3173 index 665252327aa..e64910fd4aa 100644
3174 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
3175 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
3176 @@ -1,5 +1,5 @@
3177  /* { dg-do compile } */
3178 -/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
3179 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
3180  
3181  void func0 (void);
3182  void func1 (void);
3183 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
3184 index 68c0ff713b3..365cf2ee226 100644
3185 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
3186 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
3187 @@ -1,5 +1,5 @@
3188  /* { dg-do compile } */
3189 -/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
3190 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
3191  
3192  typedef void (*dispatch_t)(long offset);
3193  
3194 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
3195 index e2da1fcb683..72646a4960b 100644
3196 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
3197 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
3198 @@ -1,5 +1,5 @@
3199  /* { dg-do compile } */
3200 -/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
3201 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
3202  
3203  typedef void (*dispatch_t)(long offset);
3204  
3205 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
3206 index 244fec708d6..f48945e3dfc 100644
3207 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
3208 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
3209 @@ -1,5 +1,5 @@
3210  /* { dg-do compile } */
3211 -/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
3212 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
3213  
3214  typedef void (*dispatch_t)(long offset);
3215  
3216 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
3217 index 107ebe32f54..4b1d558fc4e 100644
3218 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
3219 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
3220 @@ -1,5 +1,5 @@
3221  /* { dg-do compile } */
3222 -/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
3223 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
3224  
3225  typedef void (*dispatch_t)(long offset);
3226  
3227 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
3228 index 17b04ef2229..0f687c3b027 100644
3229 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
3230 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
3231 @@ -1,5 +1,5 @@
3232  /* { dg-do compile { target *-*-linux* } } */
3233 -/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
3234 +/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
3235  
3236  extern void bar (void);
3237  
3238 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
3239 index d9eb11285aa..b27c6fc96a2 100644
3240 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
3241 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
3242 @@ -1,5 +1,5 @@
3243  /* { dg-do compile { target *-*-linux* } } */
3244 -/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
3245 +/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
3246  
3247  extern void bar (void);
3248  
3249 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
3250 index d02b1dcb1b9..2c496492eaa 100644
3251 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
3252 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
3253 @@ -1,5 +1,5 @@
3254  /* { dg-do compile } */
3255 -/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
3256 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
3257  
3258  void func0 (void);
3259  void func1 (void);
3260 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-1.c b/gcc/testsuite/gcc.target/i386/ret-thunk-1.c
3261 new file mode 100644
3262 index 00000000000..7223f67ba5e
3263 --- /dev/null
3264 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-1.c
3265 @@ -0,0 +1,13 @@
3266 +/* { dg-do compile } */
3267 +/* { dg-options "-O2 -mfunction-return=thunk" } */
3268 +
3269 +void
3270 +foo (void)
3271 +{
3272 +}
3273 +
3274 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
3275 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3276 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3277 +/* { dg-final { scan-assembler {\tpause} } } */
3278 +/* { dg-final { scan-assembler {\tlfence} } } */
3279 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
3280 new file mode 100644
3281 index 00000000000..1630e2fa2b5
3282 --- /dev/null
3283 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
3284 @@ -0,0 +1,23 @@
3285 +/* { dg-do compile } */
3286 +/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
3287 +
3288 +extern void (*bar) (void);
3289 +
3290 +int
3291 +foo (void)
3292 +{
3293 +  bar ();
3294 +  return 0;
3295 +}
3296 +
3297 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3298 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3299 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
3300 +/* { dg-final { scan-assembler-times {\tpause} 2 } } */
3301 +/* { dg-final { scan-assembler-times {\tlfence} 2 } } */
3302 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
3303 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } }  } } */
3304 +/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } }  } } */
3305 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } }  } } */
3306 +/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } }  } } */
3307 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
3308 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
3309 new file mode 100644
3310 index 00000000000..876159cf783
3311 --- /dev/null
3312 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
3313 @@ -0,0 +1,23 @@
3314 +/* { dg-do compile } */
3315 +/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */
3316 +
3317 +extern void (*bar) (void);
3318 +
3319 +int
3320 +foo (void)
3321 +{
3322 +  bar ();
3323 +  return 0;
3324 +}
3325 +
3326 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
3327 +/* { dg-final { scan-assembler-times {\tpause} 1 } } */
3328 +/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
3329 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3330 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3331 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
3332 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
3333 +/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } }  } } */
3334 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } }  } } */
3335 +/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } }  } } */
3336 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
3337 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
3338 new file mode 100644
3339 index 00000000000..01b0a02f80b
3340 --- /dev/null
3341 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
3342 @@ -0,0 +1,22 @@
3343 +/* { dg-do compile } */
3344 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
3345 +
3346 +extern void (*bar) (void);
3347 +
3348 +int
3349 +foo (void)
3350 +{
3351 +  bar ();
3352 +  return 0;
3353 +}
3354 +
3355 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
3356 +/* { dg-final { scan-assembler-times {\tpause} 1 } } */
3357 +/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
3358 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3359 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3360 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
3361 +/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } }  } } */
3362 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } }  } } */
3363 +/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } }  } } */
3364 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
3365 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
3366 new file mode 100644
3367 index 00000000000..e028c2b6a99
3368 --- /dev/null
3369 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
3370 @@ -0,0 +1,22 @@
3371 +/* { dg-do compile } */
3372 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
3373 +
3374 +extern void (*bar) (void);
3375 +extern int foo (void) __attribute__ ((function_return("thunk")));
3376 +
3377 +int
3378 +foo (void)
3379 +{
3380 +  bar ();
3381 +  return 0;
3382 +}
3383 +
3384 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
3385 +/* { dg-final { scan-assembler-times {\tpause} 2 } } */
3386 +/* { dg-final { scan-assembler-times {\tlfence} 2 } } */
3387 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
3388 +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 3 } } */
3389 +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 3 } } */
3390 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_indirect_thunk" } } */
3391 +/* { dg-final { scan-assembler-not "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } }  } } */
3392 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
3393 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
3394 new file mode 100644
3395 index 00000000000..c14ee3ae4c0
3396 --- /dev/null
3397 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
3398 @@ -0,0 +1,22 @@
3399 +/* { dg-do compile } */
3400 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
3401 +
3402 +extern void (*bar) (void);
3403 +
3404 +__attribute__ ((function_return("thunk-inline")))
3405 +int
3406 +foo (void)
3407 +{
3408 +  bar ();
3409 +  return 0;
3410 +}
3411 +
3412 +/* { dg-final { scan-assembler-times {\tpause} 1 } } */
3413 +/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
3414 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
3415 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3416 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3417 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
3418 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
3419 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } }  } } */
3420 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
3421 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
3422 new file mode 100644
3423 index 00000000000..2f21e138ec2
3424 --- /dev/null
3425 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
3426 @@ -0,0 +1,22 @@
3427 +/* { dg-do compile } */
3428 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */
3429 +
3430 +extern void (*bar) (void);
3431 +
3432 +__attribute__ ((function_return("thunk-extern"), indirect_branch("thunk")))
3433 +int
3434 +foo (void)
3435 +{
3436 +  bar ();
3437 +  return 0;
3438 +}
3439 +
3440 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
3441 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3442 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3443 +/* { dg-final { scan-assembler-times {\tpause} 1 } } */
3444 +/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
3445 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
3446 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
3447 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
3448 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
3449 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-16.c b/gcc/testsuite/gcc.target/i386/ret-thunk-16.c
3450 new file mode 100644
3451 index 00000000000..a16cad16aaa
3452 --- /dev/null
3453 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-16.c
3454 @@ -0,0 +1,18 @@
3455 +/* { dg-do compile } */
3456 +/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk-extern -fno-pic" } */
3457 +
3458 +extern void (*bar) (void);
3459 +
3460 +__attribute__ ((function_return("keep"), indirect_branch("keep")))
3461 +int
3462 +foo (void)
3463 +{
3464 +  bar ();
3465 +  return 0;
3466 +}
3467 +
3468 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
3469 +/* { dg-final { scan-assembler-not "__x86_return_thunk" } } */
3470 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
3471 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
3472 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
3473 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-2.c b/gcc/testsuite/gcc.target/i386/ret-thunk-2.c
3474 new file mode 100644
3475 index 00000000000..c6659e3ad09
3476 --- /dev/null
3477 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-2.c
3478 @@ -0,0 +1,13 @@
3479 +/* { dg-do compile } */
3480 +/* { dg-options "-O2 -mfunction-return=thunk-inline" } */
3481 +
3482 +void
3483 +foo (void)
3484 +{
3485 +}
3486 +
3487 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3488 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3489 +/* { dg-final { scan-assembler {\tpause} } } */
3490 +/* { dg-final { scan-assembler {\tlfence} } } */
3491 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
3492 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-3.c b/gcc/testsuite/gcc.target/i386/ret-thunk-3.c
3493 new file mode 100644
3494 index 00000000000..0f7f388f459
3495 --- /dev/null
3496 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-3.c
3497 @@ -0,0 +1,12 @@
3498 +/* { dg-do compile } */
3499 +/* { dg-options "-O2 -mfunction-return=thunk-extern" } */
3500 +
3501 +void
3502 +foo (void)
3503 +{
3504 +}
3505 +
3506 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
3507 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
3508 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
3509 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
3510 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-4.c b/gcc/testsuite/gcc.target/i386/ret-thunk-4.c
3511 new file mode 100644
3512 index 00000000000..9ae37e835a0
3513 --- /dev/null
3514 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-4.c
3515 @@ -0,0 +1,12 @@
3516 +/* { dg-do compile } */
3517 +/* { dg-options "-O2 -mfunction-return=keep" } */
3518 +
3519 +void
3520 +foo (void)
3521 +{
3522 +}
3523 +
3524 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
3525 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
3526 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
3527 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
3528 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-5.c b/gcc/testsuite/gcc.target/i386/ret-thunk-5.c
3529 new file mode 100644
3530 index 00000000000..4bd0d2a27bc
3531 --- /dev/null
3532 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-5.c
3533 @@ -0,0 +1,15 @@
3534 +/* { dg-do compile } */
3535 +/* { dg-options "-O2 -mfunction-return=keep" } */
3536 +
3537 +extern void foo (void) __attribute__ ((function_return("thunk")));
3538 +
3539 +void
3540 +foo (void)
3541 +{
3542 +}
3543 +
3544 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
3545 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3546 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3547 +/* { dg-final { scan-assembler {\tpause} } } */
3548 +/* { dg-final { scan-assembler {\tlfence} } } */
3549 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-6.c b/gcc/testsuite/gcc.target/i386/ret-thunk-6.c
3550 new file mode 100644
3551 index 00000000000..053841f6f7d
3552 --- /dev/null
3553 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-6.c
3554 @@ -0,0 +1,14 @@
3555 +/* { dg-do compile } */
3556 +/* { dg-options "-O2 -mfunction-return=keep" } */
3557 +
3558 +__attribute__ ((function_return("thunk-inline")))
3559 +void
3560 +foo (void)
3561 +{
3562 +}
3563 +
3564 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3565 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3566 +/* { dg-final { scan-assembler {\tpause} } } */
3567 +/* { dg-final { scan-assembler {\tlfence} } } */
3568 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
3569 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-7.c b/gcc/testsuite/gcc.target/i386/ret-thunk-7.c
3570 new file mode 100644
3571 index 00000000000..262e6780112
3572 --- /dev/null
3573 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-7.c
3574 @@ -0,0 +1,13 @@
3575 +/* { dg-do compile } */
3576 +/* { dg-options "-O2 -mfunction-return=keep" } */
3577 +
3578 +__attribute__ ((function_return("thunk-extern")))
3579 +void
3580 +foo (void)
3581 +{
3582 +}
3583 +
3584 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
3585 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
3586 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
3587 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
3588 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-8.c b/gcc/testsuite/gcc.target/i386/ret-thunk-8.c
3589 new file mode 100644
3590 index 00000000000..c1658e96673
3591 --- /dev/null
3592 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-8.c
3593 @@ -0,0 +1,14 @@
3594 +/* { dg-do compile } */
3595 +/* { dg-options "-O2 -mfunction-return=thunk-inline" } */
3596 +
3597 +extern void foo (void) __attribute__ ((function_return("keep")));
3598 +
3599 +void
3600 +foo (void)
3601 +{
3602 +}
3603 +
3604 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
3605 +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
3606 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
3607 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
3608 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
3609 new file mode 100644
3610 index 00000000000..f6ccad98da7
3611 --- /dev/null
3612 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
3613 @@ -0,0 +1,25 @@
3614 +/* { dg-do compile } */
3615 +/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
3616 +
3617 +extern void (*bar) (void);
3618 +
3619 +int
3620 +foo (void)
3621 +{
3622 +  bar ();
3623 +  return 0;
3624 +}
3625 +
3626 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
3627 +/* { dg-final { scan-assembler-not "__x86_return_thunk:" } } */
3628 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
3629 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
3630 +/* { dg-final { scan-assembler "__x86_indirect_thunk:" } } */
3631 +/* { dg-final { scan-assembler-times {\tpause} 1 { target { ! x32 } } } } */
3632 +/* { dg-final { scan-assembler-times {\tlfence} 1 { target { ! x32 } } } } */
3633 +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
3634 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
3635 +/* { dg-final { scan-assembler-times {\tpause} 2 { target { x32 } } } } */
3636 +/* { dg-final { scan-assembler-times {\tlfence} 2 { target { x32 } } } } */
3637 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
3638 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
3639
3640 commit 443f274c129f9a4da28d1f796744d1179ec2fdc4
3641 Author: H.J. Lu <hjl.tools@gmail.com>
3642 Date:   Sat Jan 6 22:29:56 2018 -0800
3643
3644     x86: Add -mindirect-branch-register
3645     
3646     Add -mindirect-branch-register to force indirect branch via register.
3647     This is implemented by disabling patterns of indirect branch via memory,
3648     similar to TARGET_X32.
3649     
3650     -mindirect-branch= and -mfunction-return= tests are updated with
3651     -mno-indirect-branch-register to avoid false test failures when
3652     -mindirect-branch-register is added to RUNTESTFLAGS for "make check".
3653     
3654     gcc/
3655     
3656             * config/i386/constraints.md (Bs): Disallow memory operand for
3657             -mindirect-branch-register.
3658             (Bw): Likewise.
3659             * config/i386/predicates.md (indirect_branch_operand): Likewise.
3660             (GOT_memory_operand): Likewise.
3661             (call_insn_operand): Likewise.
3662             (sibcall_insn_operand): Likewise.
3663             (GOT32_symbol_operand): Likewise.
3664             * config/i386/i386.md (indirect_jump): Call convert_memory_address
3665             for -mindirect-branch-register.
3666             (tablejump): Likewise.
3667             (*sibcall_memory): Likewise.
3668             (*sibcall_value_memory): Likewise.
3669             Disallow peepholes of indirect call and jump via memory for
3670             -mindirect-branch-register.
3671             (*call_pop): Replace m with Bw.
3672             (*call_value_pop): Likewise.
3673             (*sibcall_pop_memory): Replace m with Bs.
3674             * config/i386/i386.opt (mindirect-branch-register): New option.
3675             * doc/invoke.texi: Document -mindirect-branch-register option.
3676     
3677     gcc/testsuite/
3678     
3679             * gcc.target/i386/indirect-thunk-1.c (dg-options): Add
3680             -mno-indirect-branch-register.
3681             * gcc.target/i386/indirect-thunk-2.c: Likewise.
3682             * gcc.target/i386/indirect-thunk-3.c: Likewise.
3683             * gcc.target/i386/indirect-thunk-4.c: Likewise.
3684             * gcc.target/i386/indirect-thunk-5.c: Likewise.
3685             * gcc.target/i386/indirect-thunk-6.c: Likewise.
3686             * gcc.target/i386/indirect-thunk-7.c: Likewise.
3687             * gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
3688             * gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
3689             * gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
3690             * gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
3691             * gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
3692             * gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
3693             * gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
3694             * gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
3695             * gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
3696             * gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
3697             * gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
3698             * gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
3699             * gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
3700             * gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
3701             * gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
3702             * gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
3703             * gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
3704             * gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
3705             * gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
3706             * gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
3707             * gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
3708             * gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
3709             * gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
3710             * gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
3711             * gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
3712             * gcc.target/i386/ret-thunk-10.c: Likewise.
3713             * gcc.target/i386/ret-thunk-11.c: Likewise.
3714             * gcc.target/i386/ret-thunk-12.c: Likewise.
3715             * gcc.target/i386/ret-thunk-13.c: Likewise.
3716             * gcc.target/i386/ret-thunk-14.c: Likewise.
3717             * gcc.target/i386/ret-thunk-15.c: Likewise.
3718             * gcc.target/i386/ret-thunk-9.c: Likewise.
3719             * gcc.target/i386/indirect-thunk-register-1.c: New test.
3720             * gcc.target/i386/indirect-thunk-register-2.c: Likewise.
3721             * gcc.target/i386/indirect-thunk-register-3.c: Likewise.
3722
3723 diff --git a/gcc/config/i386/constraints.md b/gcc/config/i386/constraints.md
3724 index 38d604fdace..697caf704dd 100644
3725 --- a/gcc/config/i386/constraints.md
3726 +++ b/gcc/config/i386/constraints.md
3727 @@ -198,16 +198,20 @@
3728  
3729  (define_constraint "Bs"
3730    "@internal Sibcall memory operand."
3731 -  (ior (and (not (match_test "TARGET_X32"))
3732 +  (ior (and (not (match_test "TARGET_X32
3733 +                             || ix86_indirect_branch_thunk_register"))
3734             (match_operand 0 "sibcall_memory_operand"))
3735 -       (and (match_test "TARGET_X32 && Pmode == DImode")
3736 +       (and (match_test "TARGET_X32 && Pmode == DImode
3737 +                        && !ix86_indirect_branch_thunk_register")
3738             (match_operand 0 "GOT_memory_operand"))))
3739  
3740  (define_constraint "Bw"
3741    "@internal Call memory operand."
3742 -  (ior (and (not (match_test "TARGET_X32"))
3743 +  (ior (and (not (match_test "TARGET_X32
3744 +                             || ix86_indirect_branch_thunk_register"))
3745             (match_operand 0 "memory_operand"))
3746 -       (and (match_test "TARGET_X32 && Pmode == DImode")
3747 +       (and (match_test "TARGET_X32 && Pmode == DImode
3748 +                        && !ix86_indirect_branch_thunk_register")
3749             (match_operand 0 "GOT_memory_operand"))))
3750  
3751  (define_constraint "Bz"
3752 diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
3753 index 3ac79ff6ee6..02ab4d45fa6 100644
3754 --- a/gcc/config/i386/i386.md
3755 +++ b/gcc/config/i386/i386.md
3756 @@ -11623,7 +11623,7 @@
3757    [(set (pc) (match_operand 0 "indirect_branch_operand"))]
3758    ""
3759  {
3760 -  if (TARGET_X32)
3761 +  if (TARGET_X32 || ix86_indirect_branch_thunk_register)
3762      operands[0] = convert_memory_address (word_mode, operands[0]);
3763    cfun->machine->has_local_indirect_jump = true;
3764  })
3765 @@ -11677,7 +11677,7 @@
3766                                          OPTAB_DIRECT);
3767      }
3768  
3769 -  if (TARGET_X32)
3770 +  if (TARGET_X32 || ix86_indirect_branch_thunk_register)
3771      operands[0] = convert_memory_address (word_mode, operands[0]);
3772    cfun->machine->has_local_indirect_jump = true;
3773  })
3774 @@ -11869,7 +11869,7 @@
3775    [(call (mem:QI (match_operand:W 0 "memory_operand" "m"))
3776          (match_operand 1))
3777     (unspec [(const_int 0)] UNSPEC_PEEPSIB)]
3778 -  "!TARGET_X32"
3779 +  "!TARGET_X32 && !ix86_indirect_branch_thunk_register"
3780    "* return ix86_output_call_insn (insn, operands[0]);"
3781    [(set_attr "type" "call")])
3782  
3783 @@ -11878,7 +11878,9 @@
3784         (match_operand:W 1 "memory_operand"))
3785     (call (mem:QI (match_dup 0))
3786          (match_operand 3))]
3787 -  "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1))
3788 +  "!TARGET_X32
3789 +   && !ix86_indirect_branch_thunk_register
3790 +   && SIBLING_CALL_P (peep2_next_insn (1))
3791     && !reg_mentioned_p (operands[0],
3792                         CALL_INSN_FUNCTION_USAGE (peep2_next_insn (1)))"
3793    [(parallel [(call (mem:QI (match_dup 1))
3794 @@ -11891,7 +11893,9 @@
3795     (unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
3796     (call (mem:QI (match_dup 0))
3797          (match_operand 3))]
3798 -  "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2))
3799 +  "!TARGET_X32
3800 +   && !ix86_indirect_branch_thunk_register
3801 +   && SIBLING_CALL_P (peep2_next_insn (2))
3802     && !reg_mentioned_p (operands[0],
3803                         CALL_INSN_FUNCTION_USAGE (peep2_next_insn (2)))"
3804    [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
3805 @@ -11913,7 +11917,7 @@
3806  })
3807  
3808  (define_insn "*call_pop"
3809 -  [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lmBz"))
3810 +  [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lBwBz"))
3811          (match_operand 1))
3812     (set (reg:SI SP_REG)
3813         (plus:SI (reg:SI SP_REG)
3814 @@ -11933,7 +11937,7 @@
3815    [(set_attr "type" "call")])
3816  
3817  (define_insn "*sibcall_pop_memory"
3818 -  [(call (mem:QI (match_operand:SI 0 "memory_operand" "m"))
3819 +  [(call (mem:QI (match_operand:SI 0 "memory_operand" "Bs"))
3820          (match_operand 1))
3821     (set (reg:SI SP_REG)
3822         (plus:SI (reg:SI SP_REG)
3823 @@ -11987,7 +11991,9 @@
3824    [(set (match_operand:W 0 "register_operand")
3825          (match_operand:W 1 "memory_operand"))
3826     (set (pc) (match_dup 0))]
3827 -  "!TARGET_X32 && peep2_reg_dead_p (2, operands[0])"
3828 +  "!TARGET_X32
3829 +   && !ix86_indirect_branch_thunk_register
3830 +   && peep2_reg_dead_p (2, operands[0])"
3831    [(set (pc) (match_dup 1))])
3832  
3833  ;; Call subroutine, returning value in operand 0
3834 @@ -12068,7 +12074,7 @@
3835         (call (mem:QI (match_operand:W 1 "memory_operand" "m"))
3836               (match_operand 2)))
3837     (unspec [(const_int 0)] UNSPEC_PEEPSIB)]
3838 -  "!TARGET_X32"
3839 +  "!TARGET_X32 && !ix86_indirect_branch_thunk_register"
3840    "* return ix86_output_call_insn (insn, operands[1]);"
3841    [(set_attr "type" "callv")])
3842  
3843 @@ -12078,7 +12084,9 @@
3844     (set (match_operand 2)
3845     (call (mem:QI (match_dup 0))
3846                  (match_operand 3)))]
3847 -  "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1))
3848 +  "!TARGET_X32
3849 +   && !ix86_indirect_branch_thunk_register
3850 +   && SIBLING_CALL_P (peep2_next_insn (1))
3851     && !reg_mentioned_p (operands[0],
3852                         CALL_INSN_FUNCTION_USAGE (peep2_next_insn (1)))"
3853    [(parallel [(set (match_dup 2)
3854 @@ -12093,7 +12101,9 @@
3855     (set (match_operand 2)
3856         (call (mem:QI (match_dup 0))
3857               (match_operand 3)))]
3858 -  "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2))
3859 +  "!TARGET_X32
3860 +   && !ix86_indirect_branch_thunk_register
3861 +   && SIBLING_CALL_P (peep2_next_insn (2))
3862     && !reg_mentioned_p (operands[0],
3863                         CALL_INSN_FUNCTION_USAGE (peep2_next_insn (2)))"
3864    [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
3865 @@ -12118,7 +12128,7 @@
3866  
3867  (define_insn "*call_value_pop"
3868    [(set (match_operand 0)
3869 -       (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lmBz"))
3870 +       (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lBwBz"))
3871               (match_operand 2)))
3872     (set (reg:SI SP_REG)
3873         (plus:SI (reg:SI SP_REG)
3874 diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
3875 index b07388d95a9..852033cbb67 100644
3876 --- a/gcc/config/i386/i386.opt
3877 +++ b/gcc/config/i386/i386.opt
3878 @@ -951,3 +951,7 @@ Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
3879  
3880  EnumValue
3881  Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
3882 +
3883 +mindirect-branch-register
3884 +Target Report Var(ix86_indirect_branch_thunk_register) Init(0)
3885 +Force indirect call and jump via register.
3886 diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
3887 index 2fc2c60f6ac..a88b1d860ca 100644
3888 --- a/gcc/config/i386/predicates.md
3889 +++ b/gcc/config/i386/predicates.md
3890 @@ -635,7 +635,8 @@
3891  ;; Test for a valid operand for indirect branch.
3892  (define_predicate "indirect_branch_operand"
3893    (ior (match_operand 0 "register_operand")
3894 -       (and (not (match_test "TARGET_X32"))
3895 +       (and (not (match_test "TARGET_X32
3896 +                             || ix86_indirect_branch_thunk_register"))
3897             (match_operand 0 "memory_operand"))))
3898  
3899  ;; Return true if OP is a memory operands that can be used in sibcalls.
3900 @@ -664,7 +665,8 @@
3901  
3902  ;; Return true if OP is a GOT memory operand.
3903  (define_predicate "GOT_memory_operand"
3904 -  (match_operand 0 "memory_operand")
3905 +  (and (match_test "!ix86_indirect_branch_thunk_register")
3906 +       (match_operand 0 "memory_operand"))
3907  {
3908    op = XEXP (op, 0);
3909    return (GET_CODE (op) == CONST
3910 @@ -678,9 +680,11 @@
3911    (ior (match_test "constant_call_address_operand
3912                      (op, mode == VOIDmode ? mode : Pmode)")
3913         (match_operand 0 "call_register_no_elim_operand")
3914 -       (ior (and (not (match_test "TARGET_X32"))
3915 +       (ior (and (not (match_test "TARGET_X32
3916 +                                  || ix86_indirect_branch_thunk_register"))
3917                  (match_operand 0 "memory_operand"))
3918 -           (and (match_test "TARGET_X32 && Pmode == DImode")
3919 +           (and (match_test "TARGET_X32 && Pmode == DImode
3920 +                             && !ix86_indirect_branch_thunk_register")
3921                  (match_operand 0 "GOT_memory_operand")))))
3922  
3923  ;; Similarly, but for tail calls, in which we cannot allow memory references.
3924 @@ -688,14 +692,17 @@
3925    (ior (match_test "constant_call_address_operand
3926                      (op, mode == VOIDmode ? mode : Pmode)")
3927         (match_operand 0 "register_no_elim_operand")
3928 -       (ior (and (not (match_test "TARGET_X32"))
3929 +       (ior (and (not (match_test "TARGET_X32
3930 +                                  || ix86_indirect_branch_thunk_register"))
3931                  (match_operand 0 "sibcall_memory_operand"))
3932 -           (and (match_test "TARGET_X32 && Pmode == DImode")
3933 +           (and (match_test "TARGET_X32 && Pmode == DImode
3934 +                             && !ix86_indirect_branch_thunk_register")
3935                  (match_operand 0 "GOT_memory_operand")))))
3936  
3937  ;; Return true if OP is a 32-bit GOT symbol operand.
3938  (define_predicate "GOT32_symbol_operand"
3939 -  (match_test "GET_CODE (op) == CONST
3940 +  (match_test "!ix86_indirect_branch_thunk_register
3941 +              && GET_CODE (op) == CONST
3942                 && GET_CODE (XEXP (op, 0)) == UNSPEC
3943                 && XINT (XEXP (op, 0), 1) == UNSPEC_GOT"))
3944  
3945 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
3946 index f3eb54b1668..1e572b1f9a2 100644
3947 --- a/gcc/doc/invoke.texi
3948 +++ b/gcc/doc/invoke.texi
3949 @@ -1211,7 +1211,8 @@ See RS/6000 and PowerPC Options.
3950  -mavx256-split-unaligned-load  -mavx256-split-unaligned-store @gol
3951  -malign-data=@var{type}  -mstack-protector-guard=@var{guard} @gol
3952  -mmitigate-rop  -mgeneral-regs-only @gol
3953 --mindirect-branch=@var{choice} -mfunction-return==@var{choice}}
3954 +-mindirect-branch=@var{choice} -mfunction-return==@var{choice} @gol
3955 +-mindirect-branch-register}
3956  
3957  @emph{x86 Windows Options}
3958  @gccoptlist{-mconsole  -mcygwin  -mno-cygwin  -mdll @gol
3959 @@ -25709,6 +25710,10 @@ object file.  You can control this behavior for a specific function by
3960  using the function attribute @code{function_return}.
3961  @xref{Function Attributes}.
3962  
3963 +@item -mindirect-branch-register
3964 +@opindex -mindirect-branch-register
3965 +Force indirect call and jump via register.
3966 +
3967  @end table
3968  
3969  These @samp{-m} switches are supported in addition to the above
3970 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
3971 index f076155c91a..9eb9b273ade 100644
3972 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
3973 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
3974 @@ -1,5 +1,5 @@
3975  /* { dg-do compile } */
3976 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
3977 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
3978  
3979  typedef void (*dispatch_t)(long offset);
3980  
3981 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
3982 index d7984f592fe..c63795e4127 100644
3983 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
3984 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
3985 @@ -1,5 +1,5 @@
3986  /* { dg-do compile } */
3987 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
3988 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
3989  
3990  typedef void (*dispatch_t)(long offset);
3991  
3992 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
3993 index 3257d0a2e16..82973cda771 100644
3994 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
3995 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
3996 @@ -1,5 +1,5 @@
3997  /* { dg-do compile } */
3998 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
3999 +/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
4000  
4001  typedef void (*dispatch_t)(long offset);
4002  
4003 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
4004 index 7cab2df6474..a5f3d1cbed8 100644
4005 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
4006 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
4007 @@ -1,5 +1,5 @@
4008  /* { dg-do compile } */
4009 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
4010 +/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
4011  
4012  typedef void (*dispatch_t)(long offset);
4013  
4014 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
4015 index b4836c38d6c..fcaa18d10b7 100644
4016 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
4017 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
4018 @@ -1,5 +1,5 @@
4019  /* { dg-do compile { target *-*-linux* } } */
4020 -/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
4021 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
4022  
4023  extern void bar (void);
4024  
4025 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
4026 index 1f06bd1af74..e4649283d10 100644
4027 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
4028 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
4029 @@ -1,5 +1,5 @@
4030  /* { dg-do compile { target *-*-linux* } } */
4031 -/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
4032 +/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
4033  
4034  extern void bar (void);
4035  
4036 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
4037 index 0b3fef86a20..ebfb8aab937 100644
4038 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
4039 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
4040 @@ -1,5 +1,5 @@
4041  /* { dg-do compile } */
4042 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
4043 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
4044  
4045  void func0 (void);
4046  void func1 (void);
4047 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
4048 index 5f6cfc17b56..a08022db8e4 100644
4049 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
4050 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
4051 @@ -1,5 +1,5 @@
4052  /* { dg-do compile } */
4053 -/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
4054 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
4055  
4056  typedef void (*dispatch_t)(long offset);
4057  
4058 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
4059 index b256160ec80..b257c695ad1 100644
4060 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
4061 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
4062 @@ -1,5 +1,5 @@
4063  /* { dg-do compile } */
4064 -/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
4065 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
4066  
4067  typedef void (*dispatch_t)(long offset);
4068  
4069 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
4070 index 567c95051d6..dfb1370d23d 100644
4071 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
4072 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
4073 @@ -1,5 +1,5 @@
4074  /* { dg-do compile } */
4075 -/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
4076 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
4077  
4078  typedef void (*dispatch_t)(long offset);
4079  
4080 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
4081 index 3b662af7d5d..a6e3f6f9f2b 100644
4082 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
4083 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
4084 @@ -1,5 +1,5 @@
4085  /* { dg-do compile } */
4086 -/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
4087 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
4088  
4089  typedef void (*dispatch_t)(long offset);
4090  
4091 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
4092 index 98785a38248..4bb1c5f9220 100644
4093 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
4094 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
4095 @@ -1,5 +1,5 @@
4096  /* { dg-do compile } */
4097 -/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
4098 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
4099  
4100  typedef void (*dispatch_t)(long offset);
4101  
4102 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
4103 index a498a39e404..4e33a638862 100644
4104 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
4105 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
4106 @@ -1,5 +1,5 @@
4107  /* { dg-do compile } */
4108 -/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
4109 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
4110  
4111  typedef void (*dispatch_t)(long offset);
4112  
4113 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
4114 index 66f295d1eb6..427ba3ddbb4 100644
4115 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
4116 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
4117 @@ -1,5 +1,5 @@
4118  /* { dg-do compile } */
4119 -/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
4120 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
4121  
4122  void func0 (void);
4123  void func1 (void);
4124 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
4125 index aacb814d737..dc7143414fb 100644
4126 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
4127 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
4128 @@ -1,5 +1,5 @@
4129  /* { dg-do compile { target { ! x32 } } } */
4130 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
4131 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
4132  
4133  void (*dispatch) (char *);
4134  char buf[10];
4135 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
4136 index 7b44dda23df..737c60946f6 100644
4137 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
4138 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
4139 @@ -1,5 +1,5 @@
4140  /* { dg-do compile { target { ! x32 } } } */
4141 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
4142 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
4143  
4144  void (*dispatch) (char *);
4145  char buf[10];
4146 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
4147 index 70b4fb36eea..d34485a0010 100644
4148 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
4149 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
4150 @@ -1,5 +1,5 @@
4151  /* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
4152 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
4153 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
4154  
4155  void bar (char *);
4156  char buf[10];
4157 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
4158 index 3baf03ee77c..0e19830de4d 100644
4159 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
4160 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
4161 @@ -1,5 +1,5 @@
4162  /* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
4163 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
4164 +/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
4165  
4166  void bar (char *);
4167  char buf[10];
4168 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
4169 index 637fc3d3f4e..5c20a35ecec 100644
4170 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
4171 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
4172 @@ -1,5 +1,5 @@
4173  /* { dg-do compile } */
4174 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4175 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4176  
4177  typedef void (*dispatch_t)(long offset);
4178  
4179 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
4180 index ff9efe03fe6..b2fb6e1bcd2 100644
4181 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
4182 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
4183 @@ -1,5 +1,5 @@
4184  /* { dg-do compile } */
4185 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4186 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4187  
4188  typedef void (*dispatch_t)(long offset);
4189  
4190 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
4191 index 2686a5f2db4..9c84547cd7c 100644
4192 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
4193 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
4194 @@ -1,5 +1,5 @@
4195  /* { dg-do compile } */
4196 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4197 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4198  
4199  typedef void (*dispatch_t)(long offset);
4200  
4201 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
4202 index f07f6b214ad..457849564bb 100644
4203 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
4204 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
4205 @@ -1,5 +1,5 @@
4206  /* { dg-do compile } */
4207 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4208 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4209  
4210  typedef void (*dispatch_t)(long offset);
4211  
4212 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
4213 index 21740ac5b7f..5c07e02df6a 100644
4214 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
4215 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
4216 @@ -1,5 +1,5 @@
4217  /* { dg-do compile { target *-*-linux* } } */
4218 -/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
4219 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
4220  
4221  extern void bar (void);
4222  
4223 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
4224 index a77c1f470b8..3eb440693a0 100644
4225 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
4226 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
4227 @@ -1,5 +1,5 @@
4228  /* { dg-do compile { target *-*-linux* } } */
4229 -/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
4230 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
4231  
4232  extern void bar (void);
4233  
4234 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
4235 index e64910fd4aa..d4747ea0764 100644
4236 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
4237 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
4238 @@ -1,5 +1,5 @@
4239  /* { dg-do compile } */
4240 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4241 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4242  
4243  void func0 (void);
4244  void func1 (void);
4245 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
4246 index 365cf2ee226..536abfa74e4 100644
4247 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
4248 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
4249 @@ -1,5 +1,5 @@
4250  /* { dg-do compile } */
4251 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4252 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4253  
4254  typedef void (*dispatch_t)(long offset);
4255  
4256 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
4257 index 72646a4960b..bd2b6246aa1 100644
4258 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
4259 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
4260 @@ -1,5 +1,5 @@
4261  /* { dg-do compile } */
4262 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4263 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4264  
4265  typedef void (*dispatch_t)(long offset);
4266  
4267 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
4268 index f48945e3dfc..9885eebbcff 100644
4269 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
4270 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
4271 @@ -1,5 +1,5 @@
4272  /* { dg-do compile } */
4273 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4274 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4275  
4276  typedef void (*dispatch_t)(long offset);
4277  
4278 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
4279 index 4b1d558fc4e..7b3983949d2 100644
4280 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
4281 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
4282 @@ -1,5 +1,5 @@
4283  /* { dg-do compile } */
4284 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4285 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4286  
4287  typedef void (*dispatch_t)(long offset);
4288  
4289 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
4290 index 0f687c3b027..c6d77e10352 100644
4291 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
4292 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
4293 @@ -1,5 +1,5 @@
4294  /* { dg-do compile { target *-*-linux* } } */
4295 -/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
4296 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
4297  
4298  extern void bar (void);
4299  
4300 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
4301 index b27c6fc96a2..6454827b780 100644
4302 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
4303 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
4304 @@ -1,5 +1,5 @@
4305  /* { dg-do compile { target *-*-linux* } } */
4306 -/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
4307 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
4308  
4309  extern void bar (void);
4310  
4311 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
4312 index 2c496492eaa..cc592f89aba 100644
4313 --- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
4314 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
4315 @@ -1,5 +1,5 @@
4316  /* { dg-do compile } */
4317 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4318 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4319  
4320  void func0 (void);
4321  void func1 (void);
4322 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
4323 new file mode 100644
4324 index 00000000000..7d396a31953
4325 --- /dev/null
4326 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
4327 @@ -0,0 +1,22 @@
4328 +/* { dg-do compile } */
4329 +/* { dg-options "-O2 -mindirect-branch=thunk -mindirect-branch-register -fno-pic" } */
4330 +
4331 +typedef void (*dispatch_t)(long offset);
4332 +
4333 +dispatch_t dispatch;
4334 +
4335 +void
4336 +male_indirect_jump (long offset)
4337 +{
4338 +  dispatch(offset);
4339 +}
4340 +
4341 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } */
4342 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
4343 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
4344 +/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } */
4345 +/* { dg-final { scan-assembler {\tpause} } } */
4346 +/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch"  } } */
4347 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
4348 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk\n" } } */
4349 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk_bnd\n" } } */
4350 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
4351 new file mode 100644
4352 index 00000000000..e7e616bb271
4353 --- /dev/null
4354 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
4355 @@ -0,0 +1,20 @@
4356 +/* { dg-do compile } */
4357 +/* { dg-options "-O2 -mindirect-branch=thunk-inline -mindirect-branch-register -fno-pic" } */
4358 +
4359 +typedef void (*dispatch_t)(long offset);
4360 +
4361 +dispatch_t dispatch;
4362 +
4363 +void
4364 +male_indirect_jump (long offset)
4365 +{
4366 +  dispatch(offset);
4367 +}
4368 +
4369 +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
4370 +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
4371 +/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } */
4372 +/* { dg-final { scan-assembler {\tpause} } } */
4373 +/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch"  } } */
4374 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
4375 +/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
4376 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
4377 new file mode 100644
4378 index 00000000000..5320e923be2
4379 --- /dev/null
4380 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
4381 @@ -0,0 +1,19 @@
4382 +/* { dg-do compile } */
4383 +/* { dg-options "-O2 -mindirect-branch=thunk-extern -mindirect-branch-register -fno-pic" } */
4384 +
4385 +typedef void (*dispatch_t)(long offset);
4386 +
4387 +dispatch_t dispatch;
4388 +
4389 +void
4390 +male_indirect_jump (long offset)
4391 +{
4392 +  dispatch(offset);
4393 +}
4394 +
4395 +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } */
4396 +/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch"  } } */
4397 +/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
4398 +/* { dg-final { scan-assembler-not {\t(pause|pause|nop)} } } */
4399 +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
4400 +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
4401 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
4402 index 1630e2fa2b5..b4f9d48065d 100644
4403 --- a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
4404 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
4405 @@ -1,5 +1,5 @@
4406  /* { dg-do compile } */
4407 -/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
4408 +/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
4409  
4410  extern void (*bar) (void);
4411  
4412 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
4413 index 876159cf783..0312577a043 100644
4414 --- a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
4415 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
4416 @@ -1,5 +1,5 @@
4417  /* { dg-do compile } */
4418 -/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */
4419 +/* { 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" } */
4420  
4421  extern void (*bar) (void);
4422  
4423 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
4424 index 01b0a02f80b..fa3181303c9 100644
4425 --- a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
4426 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
4427 @@ -1,5 +1,5 @@
4428  /* { dg-do compile } */
4429 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
4430 +/* { 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" } */
4431  
4432  extern void (*bar) (void);
4433  
4434 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
4435 index e028c2b6a99..7a08e71c76b 100644
4436 --- a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
4437 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
4438 @@ -1,5 +1,5 @@
4439  /* { dg-do compile } */
4440 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4441 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
4442  
4443  extern void (*bar) (void);
4444  extern int foo (void) __attribute__ ((function_return("thunk")));
4445 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
4446 index c14ee3ae4c0..dacf0c769fc 100644
4447 --- a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
4448 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
4449 @@ -1,5 +1,5 @@
4450  /* { dg-do compile } */
4451 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4452 +/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
4453  
4454  extern void (*bar) (void);
4455  
4456 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
4457 index 2f21e138ec2..cf06a5f35c7 100644
4458 --- a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
4459 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
4460 @@ -1,5 +1,5 @@
4461  /* { dg-do compile } */
4462 -/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */
4463 +/* { 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" } */
4464  
4465  extern void (*bar) (void);
4466  
4467 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
4468 index f6ccad98da7..6da5ab97081 100644
4469 --- a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
4470 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
4471 @@ -1,5 +1,5 @@
4472  /* { dg-do compile } */
4473 -/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
4474 +/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
4475  
4476  extern void (*bar) (void);
4477  
4478
4479 commit 92cf48982b587b20c78ede2a456151d2f497997d
4480 Author: H.J. Lu <hjl.tools@gmail.com>
4481 Date:   Sat Jan 6 22:29:56 2018 -0800
4482
4483     x86: Add 'V' register operand modifier
4484     
4485     Add 'V', a special modifier which prints the name of the full integer
4486     register without '%'.  For
4487     
4488     extern void (*func_p) (void);
4489     
4490     void
4491     foo (void)
4492     {
4493       asm ("call __x86_indirect_thunk_%V0" : : "a" (func_p));
4494     }
4495     
4496     it generates:
4497     
4498     foo:
4499             movq    func_p(%rip), %rax
4500             call    __x86_indirect_thunk_rax
4501             ret
4502     
4503     gcc/
4504     
4505             * config/i386/i386.c (print_reg): Print the name of the full
4506             integer register without '%'.
4507             (ix86_print_operand): Handle 'V'.
4508              * doc/extend.texi: Document 'V' modifier.
4509     
4510     gcc/testsuite/
4511     
4512             * gcc.target/i386/indirect-thunk-register-4.c: New test.
4513
4514 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
4515 index 4bfe2fa8c1d..e32de13688a 100644
4516 --- a/gcc/config/i386/i386.c
4517 +++ b/gcc/config/i386/i386.c
4518 @@ -17925,6 +17925,7 @@ put_condition_code (enum rtx_code code, machine_mode mode, bool reverse,
4519     If CODE is 'h', pretend the reg is the 'high' byte register.
4520     If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op.
4521     If CODE is 'd', duplicate the operand for AVX instruction.
4522 +   If CODE is 'V', print naked full integer register name without %.
4523   */
4524  
4525  void
4526 @@ -17935,7 +17936,7 @@ print_reg (rtx x, int code, FILE *file)
4527    unsigned int regno;
4528    bool duplicated;
4529  
4530 -  if (ASSEMBLER_DIALECT == ASM_ATT)
4531 +  if (ASSEMBLER_DIALECT == ASM_ATT && code != 'V')
4532      putc ('%', file);
4533  
4534    if (x == pc_rtx)
4535 @@ -17983,6 +17984,14 @@ print_reg (rtx x, int code, FILE *file)
4536        return;
4537      }
4538  
4539 +  if (code == 'V')
4540 +    {
4541 +      if (GENERAL_REGNO_P (regno))
4542 +       msize = GET_MODE_SIZE (word_mode);
4543 +      else
4544 +       error ("'V' modifier on non-integer register");
4545 +    }
4546 +
4547    duplicated = code == 'd' && TARGET_AVX;
4548  
4549    switch (msize)
4550 @@ -18102,6 +18111,7 @@ print_reg (rtx x, int code, FILE *file)
4551     & -- print some in-use local-dynamic symbol name.
4552     H -- print a memory address offset by 8; used for sse high-parts
4553     Y -- print condition for XOP pcom* instruction.
4554 +   V -- print naked full integer register name without %.
4555     + -- print a branch hint as 'cs' or 'ds' prefix
4556     ; -- print a semicolon (after prefixes due to bug in older gas).
4557     ~ -- print "i" if TARGET_AVX2, "f" otherwise.
4558 @@ -18326,6 +18336,7 @@ ix86_print_operand (FILE *file, rtx x, int code)
4559         case 'X':
4560         case 'P':
4561         case 'p':
4562 +       case 'V':
4563           break;
4564  
4565         case 's':
4566 diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
4567 index 46e0a3623a6..9db9e0e27e9 100644
4568 --- a/gcc/doc/extend.texi
4569 +++ b/gcc/doc/extend.texi
4570 @@ -8778,6 +8778,9 @@ The table below shows the list of supported modifiers and their effects.
4571  @tab @code{2}
4572  @end multitable
4573  
4574 +@code{V} is a special modifier which prints the name of the full integer
4575 +register without @code{%}.
4576 +
4577  @anchor{x86floatingpointasmoperands}
4578  @subsubsection x86 Floating-Point @code{asm} Operands
4579  
4580 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
4581 new file mode 100644
4582 index 00000000000..f0cd9b75be8
4583 --- /dev/null
4584 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
4585 @@ -0,0 +1,13 @@
4586 +/* { dg-do compile } */
4587 +/* { dg-options "-O2 -mindirect-branch=keep -fno-pic" } */
4588 +
4589 +extern void (*func_p) (void);
4590 +
4591 +void
4592 +foo (void)
4593 +{
4594 +  asm("call __x86_indirect_thunk_%V0" : : "a" (func_p));
4595 +}
4596 +
4597 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_eax" { target ia32 } } } */
4598 +/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_rax" { target { ! ia32 } } } } */
4599
4600 commit a19909d946ad1b8588595fab0b59eb9d4e027838
4601 Author: H.J. Lu <hjl.tools@gmail.com>
4602 Date:   Sat Jan 13 18:01:54 2018 -0800
4603
4604     x86: Disallow -mindirect-branch=/-mfunction-return= with -mcmodel=large
4605     
4606     Since the thunk function may not be reachable in large code model,
4607     -mcmodel=large is incompatible with -mindirect-branch=thunk,
4608     -mindirect-branch=thunk-extern, -mfunction-return=thunk and
4609     -mfunction-return=thunk-extern.  Issue an error when they are used with
4610     -mcmodel=large.
4611     
4612     gcc/
4613     
4614             * config/i386/i386.c (ix86_set_indirect_branch_type): Disallow
4615             -mcmodel=large with -mindirect-branch=thunk,
4616             -mindirect-branch=thunk-extern, -mfunction-return=thunk and
4617             -mfunction-return=thunk-extern.
4618             * doc/invoke.texi: Document -mcmodel=large is incompatible with
4619             -mindirect-branch=thunk, -mindirect-branch=thunk-extern,
4620             -mfunction-return=thunk and -mfunction-return=thunk-extern.
4621     
4622     gcc/testsuite/
4623     
4624             * gcc.target/i386/indirect-thunk-10.c: New test.
4625             * gcc.target/i386/indirect-thunk-8.c: Likewise.
4626             * gcc.target/i386/indirect-thunk-9.c: Likewise.
4627             * gcc.target/i386/indirect-thunk-attr-10.c: Likewise.
4628             * gcc.target/i386/indirect-thunk-attr-11.c: Likewise.
4629             * gcc.target/i386/indirect-thunk-attr-9.c: Likewise.
4630             * gcc.target/i386/ret-thunk-17.c: Likewise.
4631             * gcc.target/i386/ret-thunk-18.c: Likewise.
4632             * gcc.target/i386/ret-thunk-19.c: Likewise.
4633             * gcc.target/i386/ret-thunk-20.c: Likewise.
4634             * gcc.target/i386/ret-thunk-21.c: Likewise.
4635
4636 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
4637 index e32de13688a..318a71840c9 100644
4638 --- a/gcc/config/i386/i386.c
4639 +++ b/gcc/config/i386/i386.c
4640 @@ -7187,6 +7187,19 @@ ix86_set_indirect_branch_type (tree fndecl)
4641         }
4642        else
4643         cfun->machine->indirect_branch_type = ix86_indirect_branch;
4644 +
4645 +      /* -mcmodel=large is not compatible with -mindirect-branch=thunk
4646 +        nor -mindirect-branch=thunk-extern.  */
4647 +      if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
4648 +         && ((cfun->machine->indirect_branch_type
4649 +              == indirect_branch_thunk_extern)
4650 +             || (cfun->machine->indirect_branch_type
4651 +                 == indirect_branch_thunk)))
4652 +       error ("%<-mindirect-branch=%s%> and %<-mcmodel=large%> are not "
4653 +              "compatible",
4654 +              ((cfun->machine->indirect_branch_type
4655 +                == indirect_branch_thunk_extern)
4656 +               ? "thunk-extern" : "thunk"));
4657      }
4658  
4659    if (cfun->machine->function_return_type == indirect_branch_unset)
4660 @@ -7212,6 +7225,19 @@ ix86_set_indirect_branch_type (tree fndecl)
4661         }
4662        else
4663         cfun->machine->function_return_type = ix86_function_return;
4664 +
4665 +      /* -mcmodel=large is not compatible with -mfunction-return=thunk
4666 +        nor -mfunction-return=thunk-extern.  */
4667 +      if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
4668 +         && ((cfun->machine->function_return_type
4669 +              == indirect_branch_thunk_extern)
4670 +             || (cfun->machine->function_return_type
4671 +                 == indirect_branch_thunk)))
4672 +       error ("%<-mfunction-return=%s%> and %<-mcmodel=large%> are not "
4673 +              "compatible",
4674 +              ((cfun->machine->function_return_type
4675 +                == indirect_branch_thunk_extern)
4676 +               ? "thunk-extern" : "thunk"));
4677      }
4678  }
4679  
4680 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
4681 index 1e572b1f9a2..6f3c344476c 100644
4682 --- a/gcc/doc/invoke.texi
4683 +++ b/gcc/doc/invoke.texi
4684 @@ -25699,6 +25699,11 @@ to external call and return thunk provided in a separate object file.
4685  You can control this behavior for a specific function by using the
4686  function attribute @code{indirect_branch}.  @xref{Function Attributes}.
4687  
4688 +Note that @option{-mcmodel=large} is incompatible with
4689 +@option{-mindirect-branch=thunk} nor
4690 +@option{-mindirect-branch=thunk-extern} since the thunk function may
4691 +not be reachable in large code model.
4692 +
4693  @item -mfunction-return=@var{choice}
4694  @opindex -mfunction-return
4695  Convert function return with @var{choice}.  The default is @samp{keep},
4696 @@ -25710,6 +25715,12 @@ object file.  You can control this behavior for a specific function by
4697  using the function attribute @code{function_return}.
4698  @xref{Function Attributes}.
4699  
4700 +Note that @option{-mcmodel=large} is incompatible with
4701 +@option{-mfunction-return=thunk} nor
4702 +@option{-mfunction-return=thunk-extern} since the thunk function may
4703 +not be reachable in large code model.
4704 +
4705 +
4706  @item -mindirect-branch-register
4707  @opindex -mindirect-branch-register
4708  Force indirect call and jump via register.
4709 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
4710 new file mode 100644
4711 index 00000000000..a0674bd2363
4712 --- /dev/null
4713 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
4714 @@ -0,0 +1,7 @@
4715 +/* { dg-do compile { target { lp64 } } } */
4716 +/* { dg-options "-O2 -mindirect-branch=thunk-inline -mfunction-return=keep -mcmodel=large" } */
4717 +
4718 +void
4719 +bar (void)
4720 +{
4721 +}
4722 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
4723 new file mode 100644
4724 index 00000000000..7a80a8986e8
4725 --- /dev/null
4726 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
4727 @@ -0,0 +1,7 @@
4728 +/* { dg-do compile { target { lp64 } } } */
4729 +/* { dg-options "-O2 -mindirect-branch=thunk -mfunction-return=keep -mcmodel=large" } */
4730 +
4731 +void
4732 +bar (void)
4733 +{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */
4734 +}
4735 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
4736 new file mode 100644
4737 index 00000000000..d4d45c5114d
4738 --- /dev/null
4739 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
4740 @@ -0,0 +1,7 @@
4741 +/* { dg-do compile { target { lp64 } } } */
4742 +/* { dg-options "-O2 -mindirect-branch=thunk-extern -mfunction-return=keep -mcmodel=large" } */
4743 +
4744 +void
4745 +bar (void)
4746 +{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */
4747 +}
4748 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
4749 new file mode 100644
4750 index 00000000000..3a2aeaddbc5
4751 --- /dev/null
4752 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
4753 @@ -0,0 +1,9 @@
4754 +/* { dg-do compile { target { lp64 } } } */
4755 +/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
4756 +/* { dg-additional-options "-fPIC" { target fpic } } */
4757 +
4758 +__attribute__ ((indirect_branch("thunk-extern")))
4759 +void
4760 +bar (void)
4761 +{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */
4762 +}
4763 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
4764 new file mode 100644
4765 index 00000000000..8e52f032b6c
4766 --- /dev/null
4767 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
4768 @@ -0,0 +1,9 @@
4769 +/* { dg-do compile { target { lp64 } } } */
4770 +/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
4771 +/* { dg-additional-options "-fPIC" { target fpic } } */
4772 +
4773 +__attribute__ ((indirect_branch("thunk-inline")))
4774 +void
4775 +bar (void)
4776 +{
4777 +}
4778 diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
4779 new file mode 100644
4780 index 00000000000..bdaa4f6911b
4781 --- /dev/null
4782 +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
4783 @@ -0,0 +1,9 @@
4784 +/* { dg-do compile { target { lp64 } } } */
4785 +/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
4786 +/* { dg-additional-options "-fPIC" { target fpic } } */
4787 +
4788 +__attribute__ ((indirect_branch("thunk")))
4789 +void
4790 +bar (void)
4791 +{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */
4792 +}
4793 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-17.c b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
4794 new file mode 100644
4795 index 00000000000..0605e2c6542
4796 --- /dev/null
4797 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
4798 @@ -0,0 +1,7 @@
4799 +/* { dg-do compile { target { lp64 } } } */
4800 +/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=keep -mcmodel=large" } */
4801 +
4802 +void
4803 +bar (void)
4804 +{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */
4805 +}
4806 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-18.c b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
4807 new file mode 100644
4808 index 00000000000..307019dc242
4809 --- /dev/null
4810 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
4811 @@ -0,0 +1,8 @@
4812 +/* { dg-do compile { target { lp64 } } } */
4813 +/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=keep -mcmodel=large" } */
4814 +/* { dg-additional-options "-fPIC" { target fpic } } */
4815 +
4816 +void
4817 +bar (void)
4818 +{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */
4819 +}
4820 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-19.c b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
4821 new file mode 100644
4822 index 00000000000..772617f4010
4823 --- /dev/null
4824 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
4825 @@ -0,0 +1,8 @@
4826 +/* { dg-do compile { target { lp64 } } } */
4827 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
4828 +
4829 +__attribute__ ((function_return("thunk")))
4830 +void
4831 +bar (void)
4832 +{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */
4833 +}
4834 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-20.c b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
4835 new file mode 100644
4836 index 00000000000..1e9f9bd5a66
4837 --- /dev/null
4838 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
4839 @@ -0,0 +1,9 @@
4840 +/* { dg-do compile { target { lp64 } } } */
4841 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
4842 +/* { dg-additional-options "-fPIC" { target fpic } } */
4843 +
4844 +__attribute__ ((function_return("thunk-extern")))
4845 +void
4846 +bar (void)
4847 +{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */
4848 +}
4849 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-21.c b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
4850 new file mode 100644
4851 index 00000000000..eea07f7abe1
4852 --- /dev/null
4853 +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
4854 @@ -0,0 +1,9 @@
4855 +/* { dg-do compile { target { lp64 } } } */
4856 +/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
4857 +/* { dg-additional-options "-fPIC" { target fpic } } */
4858 +
4859 +__attribute__ ((function_return("thunk-inline")))
4860 +void
4861 +bar (void)
4862 +{
4863 +}
This page took 0.889879 seconds and 3 git commands to generate.