@@ -228,7 +228,12 @@
(ior (and (not (match_test "TARGET_INDIRECT_BRANCH_REGISTER"))
(not (match_test "TARGET_X32"))
(match_operand 0 "sibcall_memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (ior (and (match_test "TARGET_LP64")
+ (match_test "cfun->machine->func_type
+ == TYPE_NORMAL")
+ (match_test "cfun->machine->indirect_branch_type
+ != indirect_branch_keep"))
+ (match_test "TARGET_X32 && Pmode == DImode"))
(match_operand 0 "GOT_memory_operand"))))
(define_constraint "Bw"
@@ -236,7 +241,12 @@
(ior (and (not (match_test "TARGET_INDIRECT_BRANCH_REGISTER"))
(not (match_test "TARGET_X32"))
(match_operand 0 "memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (ior (and (match_test "TARGET_LP64")
+ (match_test "cfun->machine->func_type
+ == TYPE_NORMAL")
+ (match_test "cfun->machine->indirect_branch_type
+ != indirect_branch_keep"))
+ (match_test "TARGET_X32 && Pmode == DImode"))
(match_operand 0 "GOT_memory_operand"))))
(define_constraint "Bz"
@@ -28526,7 +28526,14 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
pic_offset_table_rtx);
}
}
- else if (!TARGET_PECOFF && !TARGET_MACHO)
+ /* In 64-bit mode, -mindirect-branch= is treated as -fno-pic
+ and ix86_output_indirect_branch will convert call via PLT
+ to indirect branch via GOT slot. */
+ else if (!TARGET_PECOFF
+ && !TARGET_MACHO
+ && (!TARGET_64BIT
+ || (cfun->machine->indirect_branch_type
+ == indirect_branch_keep)))
{
if (TARGET_64BIT)
{
@@ -28553,6 +28560,30 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
fnaddr = gen_rtx_MEM (QImode, fnaddr);
}
}
+ else if (!TARGET_64BIT
+ && HAVE_AS_IX86_GOT32X
+ && !TARGET_PECOFF
+ && !TARGET_MACHO
+ && (cfun->machine->indirect_branch_type
+ != indirect_branch_keep)
+ && !flag_pic
+ && GET_CODE (addr) == SYMBOL_REF
+ && SYMBOL_REF_FUNCTION_P (addr)
+ && !SYMBOL_REF_LOCAL_P (addr)
+ && (!flag_plt
+ || (SYMBOL_REF_DECL (addr) != NULL_TREE
+ && lookup_attribute ("noplt",
+ DECL_ATTRIBUTES (SYMBOL_REF_DECL (addr))))))
+ {
+ /* In 32-bit mode, with -fno-pic -mindirect-branch=, we load
+ function's GOT slot into a register and call the external
+ function via the register. */
+ fnaddr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
+ UNSPEC_GOT);
+ fnaddr = gen_rtx_CONST (Pmode, fnaddr);
+ fnaddr = gen_const_mem (Pmode, fnaddr);
+ fnaddr = gen_rtx_MEM (QImode, fnaddr);
+ }
}
/* Skip setting up RAX register for -mskip-rax-setup when there are no
@@ -28699,7 +28730,7 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
static bool
ix86_nopic_noplt_attribute_p (rtx call_op)
{
- if (flag_pic || ix86_cmodel == CM_LARGE
+ if (ix86_cmodel == CM_LARGE
|| !(TARGET_64BIT || HAVE_AS_IX86_GOT32X)
|| TARGET_MACHO || TARGET_SEH || TARGET_PECOFF
|| SYMBOL_REF_LOCAL_P (call_op))
@@ -28707,10 +28738,16 @@ ix86_nopic_noplt_attribute_p (rtx call_op)
tree symbol_decl = SYMBOL_REF_DECL (call_op);
+ /* In 64-bit mode, -mindirect-branch= is treated as -fno-pic and
+ ix86_output_indirect_branch will convert call via PLT to indirect
+ branch via GOT slot. */
if (!flag_plt
|| (symbol_decl != NULL_TREE
&& lookup_attribute ("noplt", DECL_ATTRIBUTES (symbol_decl))))
- return true;
+ return (!flag_pic
+ || (TARGET_64BIT
+ && (cfun->machine->indirect_branch_type
+ != indirect_branch_keep)));
return false;
}
@@ -28968,6 +29005,43 @@ static void
ix86_output_indirect_branch (rtx call_op, const char *xasm,
bool sibcall_p)
{
+ /* In 64-bit mode, convert function call via GOT:
+
+ [bnd] call/jmp *foo@GOTPCREL(%rip)
+
+ to
+
+ movq foo@GOTPCREL(%rip), %r11
+ [bnd] call/jmp __x86_indirect_thunk_[bnd_]r11
+
+ with R11 as a scratch register. */
+ if (TARGET_64BIT)
+ {
+ if (MEM_P (call_op)
+ && GET_CODE (XEXP (call_op, 0)) == CONST
+ && GET_CODE (XEXP (XEXP (call_op, 0), 0)) == UNSPEC
+ && XINT (XEXP (XEXP (call_op, 0), 0), 1) == UNSPEC_GOTPCREL)
+ {
+ rtx op = XVECEXP (XEXP (XEXP (call_op, 0), 0), 0, 0);
+ if (GET_CODE (op) != SYMBOL_REF)
+ gcc_unreachable ();
+ xasm = NULL;
+ call_op = op;
+ }
+
+ if (xasm == NULL)
+ {
+ rtx xops[2];
+ xops[0] = gen_rtx_REG (word_mode, R11_REG);
+ xops[1] = call_op;
+ char movq_buf[80];
+ snprintf (movq_buf, sizeof (movq_buf), "movq\t%s",
+ "{%p1@GOTPCREL(%%rip), %0|%0, [QWORD PTR %p1@GOTPCREL[rip]]}");
+ output_asm_insn (movq_buf, xops);
+ call_op = xops[0];
+ }
+ }
+
if (REG_P (call_op))
ix86_output_indirect_branch_via_reg (call_op, sibcall_p);
else
@@ -29126,7 +29200,7 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
= (!TARGET_SEH
&& cfun->machine->indirect_branch_type != indirect_branch_keep);
bool seh_nop_p = false;
- const char *xasm;
+ const char *xasm = NULL;
if (SIBLING_CALL_P (insn))
{
@@ -29137,9 +29211,7 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
direct_p = false;
if (TARGET_64BIT)
{
- if (output_indirect_p)
- xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
- else
+ if (!output_indirect_p)
xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
}
else
@@ -29209,9 +29281,7 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
direct_p = false;
if (TARGET_64BIT)
{
- if (output_indirect_p)
- xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
- else
+ if (!output_indirect_p)
xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
}
else
@@ -12575,6 +12575,23 @@
"* return ix86_output_call_insn (insn, operands[0]);"
[(set_attr "type" "call")])
+;; This covers both call and sibcall since only GOT slot is allowed.
+(define_insn "*call_got_thunk"
+ [(call (mem:QI (match_operand:DI 0 "GOT_memory_operand" "Bg"))
+ (match_operand 1))]
+ "TARGET_LP64
+ && cfun->machine->func_type == TYPE_NORMAL
+ && cfun->machine->indirect_branch_type != indirect_branch_keep"
+{
+ rtx fnaddr = gen_const_mem (DImode, XEXP (operands[0], 0));
+ return ix86_output_call_insn (insn, fnaddr);
+}
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "call")))])
+
;; This covers both call and sibcall since only GOT slot is allowed.
(define_insn "*call_got_x32"
[(call (mem:QI (zero_extend:DI
@@ -12778,6 +12795,25 @@
"* return ix86_output_call_insn (insn, operands[1]);"
[(set_attr "type" "callv")])
+;; This covers both call and sibcall since only GOT slot is allowed.
+(define_insn "*call_value_got_thunk"
+ [(set (match_operand 0)
+ (call (mem:QI
+ (match_operand:DI 1 "GOT_memory_operand" "Bg"))
+ (match_operand 2)))]
+ "TARGET_LP64
+ && cfun->machine->func_type == TYPE_NORMAL
+ && cfun->machine->indirect_branch_type != indirect_branch_keep"
+{
+ rtx fnaddr = gen_const_mem (DImode, XEXP (operands[1], 0));
+ return ix86_output_call_insn (insn, fnaddr);
+}
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "callv")))])
+
;; This covers both call and sibcall since only GOT slot is allowed.
(define_insn "*call_value_got_x32"
[(set (match_operand 0)
new file mode 100644
@@ -0,0 +1,19 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target ia32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_e" { target ia32 } } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_r" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target ia32 } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_e" { target ia32 } } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_r" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
@@ -9,10 +9,8 @@ foo (void)
bar ();
}
-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" { target x32 } } } */
-/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target x32 } } } */
-/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" { target { ! x32 } } } } */
-/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "mov(l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)" } } */
/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
/* { dg-final { scan-assembler {\tpause} } } */
@@ -10,13 +10,9 @@ foo (void)
return 0;
}
-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" { target x32 } } } */
-/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target x32 } } } */
-/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 { target x32 } } } */
-/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 { target x32 } } } */
-/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" { target { ! x32 } } } } */
-/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { ! x32 } } } } */
-/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
-/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)" } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */
/* { dg-final { scan-assembler {\tpause} } } */
/* { dg-final { scan-assembler {\tlfence} } } */
@@ -11,7 +11,7 @@ foo (void)
}
/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" } } */
-/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd_rax" { target lp64 } } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd_r" { target lp64 } } } */
/* { dg-final { scan-assembler "bnd call\[ \t\]*__x86_indirect_thunk_bnd_eax" { target ia32 } } } */
/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
@@ -12,7 +12,7 @@ foo (void)
}
/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" } } */
-/* { dg-final { scan-assembler "bnd call\[ \t\]*__x86_indirect_thunk_bnd_(r|e)ax" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*__x86_indirect_thunk_bnd_(r|e)" } } */
/* { dg-final { scan-assembler-times "bnd call\[ \t\]*\.LIND" 1 } } */
/* { dg-final { scan-assembler "bnd ret" } } */
/* { dg-final { scan-assembler {\tpause} } } */
new file mode 100644
@@ -0,0 +1,21 @@
+/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic -fno-plt" } */
+
+void bar (char *);
+char buf[10];
+
+void
+foo (void)
+{
+ bar (buf);
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target ia32 } } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*__x86_indirect_thunk_bnd_e" { target ia32 } } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*bar@GOTPCREL" { target lp64 } } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd_r" { target lp64 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
new file mode 100644
@@ -0,0 +1,22 @@
+/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic -fno-plt" } */
+
+void bar (char *);
+char buf[10];
+
+int
+foo (void)
+{
+ bar (buf);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target ia32 } } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*__x86_indirect_thunk_bnd_e" { target ia32 } } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*bar@GOTPCREL" { target lp64 } } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*__x86_indirect_thunk_bnd_r" { target lp64 } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "bnd call\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
new file mode 100644
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target ia32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_e" { target ia32 } } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_r" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
new file mode 100644
@@ -0,0 +1,19 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target ia32 } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_e" { target ia32 } } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_r" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
@@ -9,10 +9,8 @@ foo (void)
bar ();
}
-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" { target x32 } } } */
-/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target x32 } } } */
-/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" { target { ! x32 } } } } */
-/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "mov(l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)" } } */
/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
@@ -10,8 +10,8 @@ foo (void)
return 0;
}
-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" { target x32 } } } */
-/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target x32 } } } */
-/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" { target { ! x32 } } } } */
-/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
@@ -9,8 +9,7 @@ foo (void)
bar ();
}
-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" { target x32 } } } */
-/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "mov(l|q)\[ \t\]*bar@GOT" } } */
/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
/* { dg-final { scan-assembler {\tpause} } } */
@@ -10,8 +10,7 @@ foo (void)
return 0;
}
-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" { target x32 } } } */
-/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "mov(?:l|q)\[ \t\]*bar@GOT" } } */
/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
/* { dg-final { scan-assembler-times {\tpause} 1 } } */
new file mode 100644
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target ia32 } } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
new file mode 100644
@@ -0,0 +1,19 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target ia32 } } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */