]> git.pld-linux.org Git - packages/gcc.git/blame - gcc-retpoline.patch
- rel 6; fixes branch diff patch; fix in branch.sh
[packages/gcc.git] / gcc-retpoline.patch
CommitLineData
95542190
AM
1commit 9005adea32ef0cc14b3ef7ceacf5b67bf0862194
2Author: H.J. Lu <hjl.tools@gmail.com>
3Date: 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
21diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
22index 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
150diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
151index 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
231commit b721283e4f4ff378a0bee2255b7d62163eab9f1e
232Author: hjl <hjl@138bc75d-0d04-0410-961f-82ee72b054a4>
233Date: 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
249diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
250index 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
298commit 3b89cfddd6276d3f13c210ed11ef638515392a04
299Author: H.J. Lu <hjl.tools@gmail.com>
300Date: 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
311diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
312index 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
348commit c89890ab6730606ecc8d3f5937fe352341f0f713
349Author: H.J. Lu <hjl.tools@gmail.com>
350Date: 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
490diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h
491index 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
512diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
513index 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
524diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
525index 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 }
1293diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
1294index 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;
1311diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
1312index 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
1373diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
1374index 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)
1401diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
1402index 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
1422diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
1423index 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
1454diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
1455new file mode 100644
1456index 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} } } */
1480diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
1481new file mode 100644
1482index 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} } } */
1506diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
1507new file mode 100644
1508index 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} } } */
1533diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
1534new file mode 100644
1535index 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} } } */
1560diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
1561new file mode 100644
1562index 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} } } */
1583diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
1584new file mode 100644
1585index 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} } } */
1607diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
1608new file mode 100644
1609index 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} } } */
1657diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
1658new file mode 100644
1659index 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} } } */
1686diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
1687new file mode 100644
1688index 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} } } */
1713diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
1714new file mode 100644
1715index 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 } } } */
1742diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
1743new file mode 100644
1744index 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 } } } */
1770diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
1771new file mode 100644
1772index 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)} } } */
1798diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
1799new file mode 100644
1800index 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)} } } */
1825diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
1826new file mode 100644
1827index 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" } } */
1875diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
1876new file mode 100644
1877index 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" } } */
1923diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
1924new file mode 100644
1925index 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} } } */
1949diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
1950new file mode 100644
1951index 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} } } */
1976diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
1977new file mode 100644
1978index 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} } } */
2001diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
2002new file mode 100644
2003index 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} } } */
2027diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
2028new file mode 100644
2029index 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" } } */
2052diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
2053new file mode 100644
2054index 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" } } */
2077diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
2078new file mode 100644
2079index 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)} } } */
2103diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
2104new file mode 100644
2105index 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)} } } */
2129diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
2130new file mode 100644
2131index 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" } } */
2151diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
2152new file mode 100644
2153index 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)} } } */
2174diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
2175new file mode 100644
2176index 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" } } */
2223diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
2224new file mode 100644
2225index 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 } } } */
2249diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
2250new file mode 100644
2251index 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 } } } */
2275diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
2276new file mode 100644
2277index 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 } } } */
2302diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
2303new file mode 100644
2304index 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 } } } */
2329diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
2330new file mode 100644
2331index 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" } } */
2352diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
2353new file mode 100644
2354index 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" } } */
2376diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
2377new file mode 100644
2378index 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
2427commit ad272ea92bdadd7f94bc1dafb35529959d3de1f0
2428Author: H.J. Lu <hjl.tools@gmail.com>
2429Date: 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
2540diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
2541index 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
2552diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
2553index 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 }
2786diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
2787index 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;
2800diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
2801index 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")
2827diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
2828index 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)
2846diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
2847index 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
2866diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
2867index 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
2897diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
2898index 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
2908diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
2909index 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
2919diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
2920index 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
2930diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
2931index 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
2941diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
2942index 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
2952diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
2953index 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
2963diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
2964index 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);
2974diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
2975index 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
2985diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
2986index 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
2996diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
2997index 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
3007diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
3008index 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
3018diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
3019index 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
3029diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
3030index 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
3040diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
3041index 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);
3051diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
3052index 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);
3062diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
3063index 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];
3073diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
3074index 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];
3084diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
3085index 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];
3095diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
3096index 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];
3106diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
3107index 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
3117diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
3118index 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
3128diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
3129index 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
3139diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
3140index 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
3150diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
3151index 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
3161diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
3162index 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
3172diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
3173index 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);
3183diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
3184index 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
3194diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
3195index 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
3205diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
3206index 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
3216diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
3217index 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
3227diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
3228index 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
3238diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
3239index 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
3249diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
3250index 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);
3260diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-1.c b/gcc/testsuite/gcc.target/i386/ret-thunk-1.c
3261new file mode 100644
3262index 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} } } */
3279diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
3280new file mode 100644
3281index 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 } } } */
3308diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
3309new file mode 100644
3310index 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 } } } */
3337diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
3338new file mode 100644
3339index 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 } } } */
3365diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
3366new file mode 100644
3367index 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 } } } */
3393diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
3394new file mode 100644
3395index 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 } } } */
3421diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
3422new file mode 100644
3423index 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 } } } */
3449diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-16.c b/gcc/testsuite/gcc.target/i386/ret-thunk-16.c
3450new file mode 100644
3451index 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" } } */
3473diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-2.c b/gcc/testsuite/gcc.target/i386/ret-thunk-2.c
3474new file mode 100644
3475index 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" } } */
3492diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-3.c b/gcc/testsuite/gcc.target/i386/ret-thunk-3.c
3493new file mode 100644
3494index 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" } } */
3510diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-4.c b/gcc/testsuite/gcc.target/i386/ret-thunk-4.c
3511new file mode 100644
3512index 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" } } */
3528diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-5.c b/gcc/testsuite/gcc.target/i386/ret-thunk-5.c
3529new file mode 100644
3530index 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} } } */
3549diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-6.c b/gcc/testsuite/gcc.target/i386/ret-thunk-6.c
3550new file mode 100644
3551index 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" } } */
3569diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-7.c b/gcc/testsuite/gcc.target/i386/ret-thunk-7.c
3570new file mode 100644
3571index 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" } } */
3588diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-8.c b/gcc/testsuite/gcc.target/i386/ret-thunk-8.c
3589new file mode 100644
3590index 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" } } */
3608diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
3609new file mode 100644
3610index 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
3640commit 443f274c129f9a4da28d1f796744d1179ec2fdc4
3641Author: H.J. Lu <hjl.tools@gmail.com>
3642Date: 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
3723diff --git a/gcc/config/i386/constraints.md b/gcc/config/i386/constraints.md
3724index 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"
3752diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
3753index 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)
3874diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
3875index 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.
3886diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
3887index 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
3945diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
3946index 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
3970diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
3971index 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
3981diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
3982index 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
3992diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
3993index 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
4003diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
4004index 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
4014diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
4015index 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
4025diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
4026index 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
4036diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
4037index 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);
4047diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
4048index 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
4058diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
4059index 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
4069diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
4070index 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
4080diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
4081index 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
4091diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
4092index 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
4102diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
4103index 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
4113diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
4114index 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);
4124diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
4125index 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];
4135diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
4136index 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];
4146diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
4147index 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];
4157diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
4158index 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];
4168diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
4169index 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
4179diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
4180index 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
4190diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
4191index 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
4201diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
4202index 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
4212diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
4213index 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
4223diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
4224index 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
4234diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
4235index 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);
4245diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
4246index 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
4256diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
4257index 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
4267diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
4268index 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
4278diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
4279index 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
4289diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
4290index 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
4300diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
4301index 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
4311diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
4312index 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);
4322diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
4323new file mode 100644
4324index 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" } } */
4350diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
4351new file mode 100644
4352index 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" } } */
4376diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
4377new file mode 100644
4378index 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" } } */
4401diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
4402index 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
4412diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
4413index 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
4423diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
4424index 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
4434diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
4435index 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")));
4445diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
4446index 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
4456diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
4457index 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
4467diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
4468index 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
4479commit 92cf48982b587b20c78ede2a456151d2f497997d
4480Author: H.J. Lu <hjl.tools@gmail.com>
4481Date: 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
4514diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
4515index 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':
4566diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
4567index 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
4580diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
4581new file mode 100644
4582index 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
4600commit a19909d946ad1b8588595fab0b59eb9d4e027838
4601Author: H.J. Lu <hjl.tools@gmail.com>
4602Date: 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
4636diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
4637index 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
4680diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
4681index 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.
4709diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
4710new file mode 100644
4711index 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+}
4722diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
4723new file mode 100644
4724index 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+}
4735diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
4736new file mode 100644
4737index 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+}
4748diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
4749new file mode 100644
4750index 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+}
4763diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
4764new file mode 100644
4765index 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+}
4778diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
4779new file mode 100644
4780index 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+}
4793diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-17.c b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
4794new file mode 100644
4795index 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+}
4806diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-18.c b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
4807new file mode 100644
4808index 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+}
4820diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-19.c b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
4821new file mode 100644
4822index 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+}
4834diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-20.c b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
4835new file mode 100644
4836index 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+}
4849diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-21.c b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
4850new file mode 100644
4851index 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 1.359584 seconds and 4 git commands to generate.