* [PATCH 5/5] x86: Disallow -mindirect-branch=/-mfunction-return= with -mcmodel=large
2018-01-14 3:37 [PATCH 0/5] x86: CVE-2017-5715, aka Spectre H.J. Lu
@ 2018-01-14 3:37 ` H.J. Lu
2018-01-14 10:49 ` Jan Hubicka
2018-01-14 3:37 ` [PATCH 2/5] x86: Add -mfunction-return= H.J. Lu
` (4 subsequent siblings)
5 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 3:37 UTC (permalink / raw)
To: gcc-patches
Since the thunk function may not be reachable in large code model,
-mcmodel=large is incompatible with -mindirect-branch=thunk,
-mindirect-branch=thunk-extern, -mfunction-return=thunk and
-mfunction-return=thunk-extern. Issue an error when they are used with
-mcmodel=large.
gcc/
* config/i386/i386.c (ix86_set_indirect_branch_type): Disallow
-mcmodel=large with -mindirect-branch=thunk,
-mindirect-branch=thunk-extern, -mfunction-return=thunk and
-mfunction-return=thunk-extern.
* doc/invoke.texi: Document -mcmodel=large is incompatible with
-mindirect-branch=thunk, -mindirect-branch=thunk-extern,
-mfunction-return=thunk and -mfunction-return=thunk-extern.
gcc/testsuite/
* gcc.target/i386/indirect-thunk-10.c: New test.
* gcc.target/i386/indirect-thunk-8.c: Likewise.
* gcc.target/i386/indirect-thunk-9.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-10.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-11.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-9.c: Likewise.
* gcc.target/i386/ret-thunk-17.c: Likewise.
* gcc.target/i386/ret-thunk-18.c: Likewise.
* gcc.target/i386/ret-thunk-19.c: Likewise.
* gcc.target/i386/ret-thunk-20.c: Likewise.
* gcc.target/i386/ret-thunk-21.c: Likewise.
---
gcc/config/i386/i386.c | 26 ++++++++++++++++++++++
gcc/doc/invoke.texi | 11 +++++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-10.c | 7 ++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-8.c | 7 ++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-9.c | 7 ++++++
.../gcc.target/i386/indirect-thunk-attr-10.c | 9 ++++++++
.../gcc.target/i386/indirect-thunk-attr-11.c | 9 ++++++++
.../gcc.target/i386/indirect-thunk-attr-9.c | 9 ++++++++
gcc/testsuite/gcc.target/i386/ret-thunk-17.c | 7 ++++++
gcc/testsuite/gcc.target/i386/ret-thunk-18.c | 8 +++++++
gcc/testsuite/gcc.target/i386/ret-thunk-19.c | 8 +++++++
gcc/testsuite/gcc.target/i386/ret-thunk-20.c | 9 ++++++++
gcc/testsuite/gcc.target/i386/ret-thunk-21.c | 9 ++++++++
13 files changed, 126 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-17.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-18.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-19.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-20.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-21.c
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 9807de0c117..92b328ebfc2 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -5836,6 +5836,19 @@ ix86_set_indirect_branch_type (tree fndecl)
}
else
cfun->machine->indirect_branch_type = ix86_indirect_branch;
+
+ /* -mcmodel=large is not compatible with -mindirect-branch=thunk
+ nor -mindirect-branch=thunk-extern. */
+ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
+ && ((cfun->machine->indirect_branch_type
+ == indirect_branch_thunk_extern)
+ || (cfun->machine->indirect_branch_type
+ == indirect_branch_thunk)))
+ error ("%<-mindirect-branch=%s%> and %<-mcmodel=large%> are not "
+ "compatible",
+ ((cfun->machine->indirect_branch_type
+ == indirect_branch_thunk_extern)
+ ? "thunk-extern" : "thunk"));
}
if (cfun->machine->function_return_type == indirect_branch_unset)
@@ -5861,6 +5874,19 @@ ix86_set_indirect_branch_type (tree fndecl)
}
else
cfun->machine->function_return_type = ix86_function_return;
+
+ /* -mcmodel=large is not compatible with -mfunction-return=thunk
+ nor -mfunction-return=thunk-extern. */
+ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
+ && ((cfun->machine->function_return_type
+ == indirect_branch_thunk_extern)
+ || (cfun->machine->function_return_type
+ == indirect_branch_thunk)))
+ error ("%<-mfunction-return=%s%> and %<-mcmodel=large%> are not "
+ "compatible",
+ ((cfun->machine->function_return_type
+ == indirect_branch_thunk_extern)
+ ? "thunk-extern" : "thunk"));
}
}
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d16006e653a..2495a45c957 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -26851,6 +26851,11 @@ to external call and return thunk provided in a separate object file.
You can control this behavior for a specific function by using the
function attribute @code{indirect_branch}. @xref{Function Attributes}.
+Note that @option{-mcmodel=large} is incompatible with
+@option{-mindirect-branch=thunk} nor
+@option{-mindirect-branch=thunk-extern} since the thunk function may
+not be reachable in large code model.
+
@item -mfunction-return=@var{choice}
@opindex -mfunction-return
Convert function return with @var{choice}. The default is @samp{keep},
@@ -26862,6 +26867,12 @@ object file. You can control this behavior for a specific function by
using the function attribute @code{function_return}.
@xref{Function Attributes}.
+Note that @option{-mcmodel=large} is incompatible with
+@option{-mfunction-return=thunk} nor
+@option{-mfunction-return=thunk-extern} since the thunk function may
+not be reachable in large code model.
+
+
@item -mindirect-branch-register
@opindex -mindirect-branch-register
Force indirect call and jump via register.
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
new file mode 100644
index 00000000000..5623f162dea
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
new file mode 100644
index 00000000000..d6c1437bcf1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
new file mode 100644
index 00000000000..6914dac27ce
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
new file mode 100644
index 00000000000..87c7684aa92
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
new file mode 100644
index 00000000000..0499c1d7d27
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk-inline")))
+void
+bar (void)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
new file mode 100644
index 00000000000..3f1d4f54884
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk")))
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-17.c b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
new file mode 100644
index 00000000000..930f72713ae
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-18.c b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
new file mode 100644
index 00000000000..5763fde84b1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-19.c b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
new file mode 100644
index 00000000000..ff710d4c1fe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+
+__attribute__ ((function_return("thunk")))
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-20.c b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
new file mode 100644
index 00000000000..eeffbd2289a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((function_return("thunk-extern")))
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-21.c b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
new file mode 100644
index 00000000000..49ab118e04f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((function_return("thunk-inline")))
+void
+bar (void)
+{
+}
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 5/5] x86: Disallow -mindirect-branch=/-mfunction-return= with -mcmodel=large
2018-01-14 3:37 ` [PATCH 5/5] x86: Disallow -mindirect-branch=/-mfunction-return= with -mcmodel=large H.J. Lu
@ 2018-01-14 10:49 ` Jan Hubicka
2018-01-14 14:20 ` H.J. Lu
0 siblings, 1 reply; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 10:49 UTC (permalink / raw)
To: H.J. Lu; +Cc: gcc-patches
> Since the thunk function may not be reachable in large code model,
> -mcmodel=large is incompatible with -mindirect-branch=thunk,
> -mindirect-branch=thunk-extern, -mfunction-return=thunk and
> -mfunction-return=thunk-extern. Issue an error when they are used with
> -mcmodel=large.
>
> gcc/
>
> * config/i386/i386.c (ix86_set_indirect_branch_type): Disallow
> -mcmodel=large with -mindirect-branch=thunk,
> -mindirect-branch=thunk-extern, -mfunction-return=thunk and
> -mfunction-return=thunk-extern.
> * doc/invoke.texi: Document -mcmodel=large is incompatible with
> -mindirect-branch=thunk, -mindirect-branch=thunk-extern,
> -mfunction-return=thunk and -mfunction-return=thunk-extern.
>
> gcc/testsuite/
>
> * gcc.target/i386/indirect-thunk-10.c: New test.
> * gcc.target/i386/indirect-thunk-8.c: Likewise.
> * gcc.target/i386/indirect-thunk-9.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-10.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-11.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-9.c: Likewise.
> * gcc.target/i386/ret-thunk-17.c: Likewise.
> * gcc.target/i386/ret-thunk-18.c: Likewise.
> * gcc.target/i386/ret-thunk-19.c: Likewise.
> * gcc.target/i386/ret-thunk-20.c: Likewise.
> * gcc.target/i386/ret-thunk-21.c: Likewise.
OK.
Honza
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 5/5] x86: Disallow -mindirect-branch=/-mfunction-return= with -mcmodel=large
2018-01-14 10:49 ` Jan Hubicka
@ 2018-01-14 14:20 ` H.J. Lu
2018-01-14 16:08 ` H.J. Lu
0 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 14:20 UTC (permalink / raw)
To: Jan Hubicka; +Cc: GCC Patches
[-- Attachment #1: Type: text/plain, Size: 1610 bytes --]
On Sun, Jan 14, 2018 at 2:47 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Since the thunk function may not be reachable in large code model,
>> -mcmodel=large is incompatible with -mindirect-branch=thunk,
>> -mindirect-branch=thunk-extern, -mfunction-return=thunk and
>> -mfunction-return=thunk-extern. Issue an error when they are used with
>> -mcmodel=large.
>>
>> gcc/
>>
>> * config/i386/i386.c (ix86_set_indirect_branch_type): Disallow
>> -mcmodel=large with -mindirect-branch=thunk,
>> -mindirect-branch=thunk-extern, -mfunction-return=thunk and
>> -mfunction-return=thunk-extern.
>> * doc/invoke.texi: Document -mcmodel=large is incompatible with
>> -mindirect-branch=thunk, -mindirect-branch=thunk-extern,
>> -mfunction-return=thunk and -mfunction-return=thunk-extern.
>>
>> gcc/testsuite/
>>
>> * gcc.target/i386/indirect-thunk-10.c: New test.
>> * gcc.target/i386/indirect-thunk-8.c: Likewise.
>> * gcc.target/i386/indirect-thunk-9.c: Likewise.
>> * gcc.target/i386/indirect-thunk-attr-10.c: Likewise.
>> * gcc.target/i386/indirect-thunk-attr-11.c: Likewise.
>> * gcc.target/i386/indirect-thunk-attr-9.c: Likewise.
>> * gcc.target/i386/ret-thunk-17.c: Likewise.
>> * gcc.target/i386/ret-thunk-18.c: Likewise.
>> * gcc.target/i386/ret-thunk-19.c: Likewise.
>> * gcc.target/i386/ret-thunk-20.c: Likewise.
>> * gcc.target/i386/ret-thunk-21.c: Likewise.
>
> OK.
> Honza
This is the patch I am checking in to only run tests on LP64 since
-mcmodel=large doesn't work with -mx32.
Thanks.
--
H.J.
[-- Attachment #2: 0005-x86-Disallow-mindirect-branch-mfunction-return-with-.patch --]
[-- Type: text/x-patch, Size: 11918 bytes --]
From b1ccfb7ff73f3ceacc4bcc0a5737d92494ac0c37 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 13 Jan 2018 18:01:54 -0800
Subject: [PATCH 5/6] x86: Disallow -mindirect-branch=/-mfunction-return= with
-mcmodel=large
Since the thunk function may not be reachable in large code model,
-mcmodel=large is incompatible with -mindirect-branch=thunk,
-mindirect-branch=thunk-extern, -mfunction-return=thunk and
-mfunction-return=thunk-extern. Issue an error when they are used with
-mcmodel=large.
gcc/
* config/i386/i386.c (ix86_set_indirect_branch_type): Disallow
-mcmodel=large with -mindirect-branch=thunk,
-mindirect-branch=thunk-extern, -mfunction-return=thunk and
-mfunction-return=thunk-extern.
* doc/invoke.texi: Document -mcmodel=large is incompatible with
-mindirect-branch=thunk, -mindirect-branch=thunk-extern,
-mfunction-return=thunk and -mfunction-return=thunk-extern.
gcc/testsuite/
* gcc.target/i386/indirect-thunk-10.c: New test.
* gcc.target/i386/indirect-thunk-8.c: Likewise.
* gcc.target/i386/indirect-thunk-9.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-10.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-11.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-9.c: Likewise.
* gcc.target/i386/ret-thunk-17.c: Likewise.
* gcc.target/i386/ret-thunk-18.c: Likewise.
* gcc.target/i386/ret-thunk-19.c: Likewise.
* gcc.target/i386/ret-thunk-20.c: Likewise.
* gcc.target/i386/ret-thunk-21.c: Likewise.
---
gcc/config/i386/i386.c | 26 ++++++++++++++++++++++
gcc/doc/invoke.texi | 11 +++++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-10.c | 7 ++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-8.c | 7 ++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-9.c | 7 ++++++
.../gcc.target/i386/indirect-thunk-attr-10.c | 9 ++++++++
.../gcc.target/i386/indirect-thunk-attr-11.c | 9 ++++++++
.../gcc.target/i386/indirect-thunk-attr-9.c | 9 ++++++++
gcc/testsuite/gcc.target/i386/ret-thunk-17.c | 7 ++++++
gcc/testsuite/gcc.target/i386/ret-thunk-18.c | 8 +++++++
gcc/testsuite/gcc.target/i386/ret-thunk-19.c | 8 +++++++
gcc/testsuite/gcc.target/i386/ret-thunk-20.c | 9 ++++++++
gcc/testsuite/gcc.target/i386/ret-thunk-21.c | 9 ++++++++
13 files changed, 126 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-17.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-18.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-19.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-20.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-21.c
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 46cc2563859..3bdbe088182 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -5836,6 +5836,19 @@ ix86_set_indirect_branch_type (tree fndecl)
}
else
cfun->machine->indirect_branch_type = ix86_indirect_branch;
+
+ /* -mcmodel=large is not compatible with -mindirect-branch=thunk
+ nor -mindirect-branch=thunk-extern. */
+ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
+ && ((cfun->machine->indirect_branch_type
+ == indirect_branch_thunk_extern)
+ || (cfun->machine->indirect_branch_type
+ == indirect_branch_thunk)))
+ error ("%<-mindirect-branch=%s%> and %<-mcmodel=large%> are not "
+ "compatible",
+ ((cfun->machine->indirect_branch_type
+ == indirect_branch_thunk_extern)
+ ? "thunk-extern" : "thunk"));
}
if (cfun->machine->function_return_type == indirect_branch_unset)
@@ -5861,6 +5874,19 @@ ix86_set_indirect_branch_type (tree fndecl)
}
else
cfun->machine->function_return_type = ix86_function_return;
+
+ /* -mcmodel=large is not compatible with -mfunction-return=thunk
+ nor -mfunction-return=thunk-extern. */
+ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
+ && ((cfun->machine->function_return_type
+ == indirect_branch_thunk_extern)
+ || (cfun->machine->function_return_type
+ == indirect_branch_thunk)))
+ error ("%<-mfunction-return=%s%> and %<-mcmodel=large%> are not "
+ "compatible",
+ ((cfun->machine->function_return_type
+ == indirect_branch_thunk_extern)
+ ? "thunk-extern" : "thunk"));
}
}
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d16006e653a..2495a45c957 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -26851,6 +26851,11 @@ to external call and return thunk provided in a separate object file.
You can control this behavior for a specific function by using the
function attribute @code{indirect_branch}. @xref{Function Attributes}.
+Note that @option{-mcmodel=large} is incompatible with
+@option{-mindirect-branch=thunk} nor
+@option{-mindirect-branch=thunk-extern} since the thunk function may
+not be reachable in large code model.
+
@item -mfunction-return=@var{choice}
@opindex -mfunction-return
Convert function return with @var{choice}. The default is @samp{keep},
@@ -26862,6 +26867,12 @@ object file. You can control this behavior for a specific function by
using the function attribute @code{function_return}.
@xref{Function Attributes}.
+Note that @option{-mcmodel=large} is incompatible with
+@option{-mfunction-return=thunk} nor
+@option{-mfunction-return=thunk-extern} since the thunk function may
+not be reachable in large code model.
+
+
@item -mindirect-branch-register
@opindex -mindirect-branch-register
Force indirect call and jump via register.
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
new file mode 100644
index 00000000000..a0674bd2363
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
new file mode 100644
index 00000000000..7a80a8986e8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
new file mode 100644
index 00000000000..d4d45c5114d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
new file mode 100644
index 00000000000..3a2aeaddbc5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
new file mode 100644
index 00000000000..8e52f032b6c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk-inline")))
+void
+bar (void)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
new file mode 100644
index 00000000000..bdaa4f6911b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk")))
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-17.c b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
new file mode 100644
index 00000000000..0605e2c6542
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-18.c b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
new file mode 100644
index 00000000000..307019dc242
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-19.c b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
new file mode 100644
index 00000000000..772617f4010
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+
+__attribute__ ((function_return("thunk")))
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-20.c b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
new file mode 100644
index 00000000000..1e9f9bd5a66
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((function_return("thunk-extern")))
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-21.c b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
new file mode 100644
index 00000000000..eea07f7abe1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((function_return("thunk-inline")))
+void
+bar (void)
+{
+}
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 5/5] x86: Disallow -mindirect-branch=/-mfunction-return= with -mcmodel=large
2018-01-14 14:20 ` H.J. Lu
@ 2018-01-14 16:08 ` H.J. Lu
0 siblings, 0 replies; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 16:08 UTC (permalink / raw)
To: Jan Hubicka, Uros Bizjak; +Cc: GCC Patches
[-- Attachment #1: Type: text/plain, Size: 1767 bytes --]
On Sun, Jan 14, 2018 at 4:57 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Sun, Jan 14, 2018 at 2:47 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> Since the thunk function may not be reachable in large code model,
>>> -mcmodel=large is incompatible with -mindirect-branch=thunk,
>>> -mindirect-branch=thunk-extern, -mfunction-return=thunk and
>>> -mfunction-return=thunk-extern. Issue an error when they are used with
>>> -mcmodel=large.
>>>
>>> gcc/
>>>
>>> * config/i386/i386.c (ix86_set_indirect_branch_type): Disallow
>>> -mcmodel=large with -mindirect-branch=thunk,
>>> -mindirect-branch=thunk-extern, -mfunction-return=thunk and
>>> -mfunction-return=thunk-extern.
>>> * doc/invoke.texi: Document -mcmodel=large is incompatible with
>>> -mindirect-branch=thunk, -mindirect-branch=thunk-extern,
>>> -mfunction-return=thunk and -mfunction-return=thunk-extern.
>>>
>>> gcc/testsuite/
>>>
>>> * gcc.target/i386/indirect-thunk-10.c: New test.
>>> * gcc.target/i386/indirect-thunk-8.c: Likewise.
>>> * gcc.target/i386/indirect-thunk-9.c: Likewise.
>>> * gcc.target/i386/indirect-thunk-attr-10.c: Likewise.
>>> * gcc.target/i386/indirect-thunk-attr-11.c: Likewise.
>>> * gcc.target/i386/indirect-thunk-attr-9.c: Likewise.
>>> * gcc.target/i386/ret-thunk-17.c: Likewise.
>>> * gcc.target/i386/ret-thunk-18.c: Likewise.
>>> * gcc.target/i386/ret-thunk-19.c: Likewise.
>>> * gcc.target/i386/ret-thunk-20.c: Likewise.
>>> * gcc.target/i386/ret-thunk-21.c: Likewise.
>>
>> OK.
>> Honza
>
> This is the patch I am checking in to only run tests on LP64 since
> -mcmodel=large doesn't work with -mx32.
>
Here is the backport for GCC 7. OK for gcc-7-branch?
--
H.J.
[-- Attachment #2: 0005-x86-Disallow-mindirect-branch-mfunction-return-with-.patch --]
[-- Type: text/x-patch, Size: 11966 bytes --]
From 13dce7cceef28026c4fc2e505d724526141fe4c1 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 13 Jan 2018 18:01:54 -0800
Subject: [PATCH 5/5] x86: Disallow -mindirect-branch=/-mfunction-return= with
-mcmodel=large
Since the thunk function may not be reachable in large code model,
-mcmodel=large is incompatible with -mindirect-branch=thunk,
-mindirect-branch=thunk-extern, -mfunction-return=thunk and
-mfunction-return=thunk-extern. Issue an error when they are used with
-mcmodel=large.
gcc/
Backport from mainline
* config/i386/i386.c (ix86_set_indirect_branch_type): Disallow
-mcmodel=large with -mindirect-branch=thunk,
-mindirect-branch=thunk-extern, -mfunction-return=thunk and
-mfunction-return=thunk-extern.
* doc/invoke.texi: Document -mcmodel=large is incompatible with
-mindirect-branch=thunk, -mindirect-branch=thunk-extern,
-mfunction-return=thunk and -mfunction-return=thunk-extern.
gcc/testsuite/
Backport from mainline
* gcc.target/i386/indirect-thunk-10.c: New test.
* gcc.target/i386/indirect-thunk-8.c: Likewise.
* gcc.target/i386/indirect-thunk-9.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-10.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-11.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-9.c: Likewise.
* gcc.target/i386/ret-thunk-17.c: Likewise.
* gcc.target/i386/ret-thunk-18.c: Likewise.
* gcc.target/i386/ret-thunk-19.c: Likewise.
* gcc.target/i386/ret-thunk-20.c: Likewise.
* gcc.target/i386/ret-thunk-21.c: Likewise.
---
gcc/config/i386/i386.c | 26 ++++++++++++++++++++++
gcc/doc/invoke.texi | 11 +++++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-10.c | 7 ++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-8.c | 7 ++++++
gcc/testsuite/gcc.target/i386/indirect-thunk-9.c | 7 ++++++
.../gcc.target/i386/indirect-thunk-attr-10.c | 9 ++++++++
.../gcc.target/i386/indirect-thunk-attr-11.c | 9 ++++++++
.../gcc.target/i386/indirect-thunk-attr-9.c | 9 ++++++++
gcc/testsuite/gcc.target/i386/ret-thunk-17.c | 7 ++++++
gcc/testsuite/gcc.target/i386/ret-thunk-18.c | 8 +++++++
gcc/testsuite/gcc.target/i386/ret-thunk-19.c | 8 +++++++
gcc/testsuite/gcc.target/i386/ret-thunk-20.c | 9 ++++++++
gcc/testsuite/gcc.target/i386/ret-thunk-21.c | 9 ++++++++
13 files changed, 126 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-17.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-18.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-19.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-20.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-21.c
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index e32de13688a..318a71840c9 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -7187,6 +7187,19 @@ ix86_set_indirect_branch_type (tree fndecl)
}
else
cfun->machine->indirect_branch_type = ix86_indirect_branch;
+
+ /* -mcmodel=large is not compatible with -mindirect-branch=thunk
+ nor -mindirect-branch=thunk-extern. */
+ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
+ && ((cfun->machine->indirect_branch_type
+ == indirect_branch_thunk_extern)
+ || (cfun->machine->indirect_branch_type
+ == indirect_branch_thunk)))
+ error ("%<-mindirect-branch=%s%> and %<-mcmodel=large%> are not "
+ "compatible",
+ ((cfun->machine->indirect_branch_type
+ == indirect_branch_thunk_extern)
+ ? "thunk-extern" : "thunk"));
}
if (cfun->machine->function_return_type == indirect_branch_unset)
@@ -7212,6 +7225,19 @@ ix86_set_indirect_branch_type (tree fndecl)
}
else
cfun->machine->function_return_type = ix86_function_return;
+
+ /* -mcmodel=large is not compatible with -mfunction-return=thunk
+ nor -mfunction-return=thunk-extern. */
+ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
+ && ((cfun->machine->function_return_type
+ == indirect_branch_thunk_extern)
+ || (cfun->machine->function_return_type
+ == indirect_branch_thunk)))
+ error ("%<-mfunction-return=%s%> and %<-mcmodel=large%> are not "
+ "compatible",
+ ((cfun->machine->function_return_type
+ == indirect_branch_thunk_extern)
+ ? "thunk-extern" : "thunk"));
}
}
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1e572b1f9a2..6f3c344476c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -25699,6 +25699,11 @@ to external call and return thunk provided in a separate object file.
You can control this behavior for a specific function by using the
function attribute @code{indirect_branch}. @xref{Function Attributes}.
+Note that @option{-mcmodel=large} is incompatible with
+@option{-mindirect-branch=thunk} nor
+@option{-mindirect-branch=thunk-extern} since the thunk function may
+not be reachable in large code model.
+
@item -mfunction-return=@var{choice}
@opindex -mfunction-return
Convert function return with @var{choice}. The default is @samp{keep},
@@ -25710,6 +25715,12 @@ object file. You can control this behavior for a specific function by
using the function attribute @code{function_return}.
@xref{Function Attributes}.
+Note that @option{-mcmodel=large} is incompatible with
+@option{-mfunction-return=thunk} nor
+@option{-mfunction-return=thunk-extern} since the thunk function may
+not be reachable in large code model.
+
+
@item -mindirect-branch-register
@opindex -mindirect-branch-register
Force indirect call and jump via register.
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
new file mode 100644
index 00000000000..a0674bd2363
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
new file mode 100644
index 00000000000..7a80a8986e8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
new file mode 100644
index 00000000000..d4d45c5114d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -mfunction-return=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
new file mode 100644
index 00000000000..3a2aeaddbc5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
new file mode 100644
index 00000000000..8e52f032b6c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk-inline")))
+void
+bar (void)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
new file mode 100644
index 00000000000..bdaa4f6911b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((indirect_branch("thunk")))
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-17.c b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
new file mode 100644
index 00000000000..0605e2c6542
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=keep -mcmodel=large" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-18.c b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
new file mode 100644
index 00000000000..307019dc242
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-19.c b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
new file mode 100644
index 00000000000..772617f4010
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+
+__attribute__ ((function_return("thunk")))
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-20.c b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
new file mode 100644
index 00000000000..1e9f9bd5a66
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((function_return("thunk-extern")))
+void
+bar (void)
+{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-21.c b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
new file mode 100644
index 00000000000..eea07f7abe1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */
+/* { dg-additional-options "-fPIC" { target fpic } } */
+
+__attribute__ ((function_return("thunk-inline")))
+void
+bar (void)
+{
+}
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 2/5] x86: Add -mfunction-return=
2018-01-14 3:37 [PATCH 0/5] x86: CVE-2017-5715, aka Spectre H.J. Lu
2018-01-14 3:37 ` [PATCH 5/5] x86: Disallow -mindirect-branch=/-mfunction-return= with -mcmodel=large H.J. Lu
@ 2018-01-14 3:37 ` H.J. Lu
2018-01-14 10:51 ` Jan Hubicka
2018-01-14 3:37 ` [PATCH 1/5] x86: Add -mindirect-branch= H.J. Lu
` (3 subsequent siblings)
5 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 3:37 UTC (permalink / raw)
To: gcc-patches
Add -mfunction-return= option to convert function return to call and
return thunks. The default is 'keep', which keeps function return
unmodified. 'thunk' converts function return to call and return thunk.
'thunk-inline' converts function return to inlined call and return thunk.
'thunk-extern' converts function return to external call and return
thunk provided in a separate object file. You can control this behavior
for a specific function by using the function attribute function_return.
Function return thunk is the same as memory thunk for -mindirect-branch=
where the return address is at the top of the stack:
__x86_return_thunk:
call L2
L1:
pause
jmp L1
L2:
lea 8(%rsp), %rsp|lea 4(%esp), %esp
ret
and function return becomes
jmp __x86_return_thunk
-mindirect-branch= tests are updated with -mfunction-return=keep to
avoid false test failures when -mfunction-return=thunk is added to
RUNTESTFLAGS for "make check".
gcc/
* config/i386/i386-protos.h (ix86_output_function_return): New.
* config/i386/i386.c (ix86_set_indirect_branch_type): Also
set function_return_type.
(indirect_thunk_name): Add ret_p to indicate thunk for function
return.
(output_indirect_thunk_function): Pass false to
indirect_thunk_name.
(ix86_output_indirect_branch): Likewise.
(output_indirect_thunk_function): Create alias for function
return thunk if regno < 0.
(ix86_output_function_return): New function.
(ix86_handle_fndecl_attribute): Handle function_return.
(ix86_attribute_table): Add function_return.
* config/i386/i386.h (machine_function): Add
function_return_type.
* config/i386/i386.md (simple_return_internal): Use
ix86_output_function_return.
(simple_return_internal_long): Likewise.
* config/i386/i386.opt (mfunction-return=): New option.
(indirect_branch): Mention -mfunction-return=.
* doc/extend.texi: Document function_return function attribute.
* doc/invoke.texi: Document -mfunction-return= option.
gcc/testsuite/
* gcc.target/i386/indirect-thunk-1.c (dg-options): Add
-mfunction-return=keep.
* gcc.target/i386/indirect-thunk-2.c: Likewise.
* gcc.target/i386/indirect-thunk-3.c: Likewise.
* gcc.target/i386/indirect-thunk-4.c: Likewise.
* gcc.target/i386/indirect-thunk-5.c: Likewise.
* gcc.target/i386/indirect-thunk-6.c: Likewise.
* gcc.target/i386/indirect-thunk-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
* gcc.target/i386/ret-thunk-1.c: New test.
* gcc.target/i386/ret-thunk-10.c: Likewise.
* gcc.target/i386/ret-thunk-11.c: Likewise.
* gcc.target/i386/ret-thunk-12.c: Likewise.
* gcc.target/i386/ret-thunk-13.c: Likewise.
* gcc.target/i386/ret-thunk-14.c: Likewise.
* gcc.target/i386/ret-thunk-15.c: Likewise.
* gcc.target/i386/ret-thunk-16.c: Likewise.
* gcc.target/i386/ret-thunk-2.c: Likewise.
* gcc.target/i386/ret-thunk-3.c: Likewise.
* gcc.target/i386/ret-thunk-4.c: Likewise.
* gcc.target/i386/ret-thunk-5.c: Likewise.
* gcc.target/i386/ret-thunk-6.c: Likewise.
* gcc.target/i386/ret-thunk-7.c: Likewise.
* gcc.target/i386/ret-thunk-8.c: Likewise.
* gcc.target/i386/ret-thunk-9.c: Likewise.
---
gcc/config/i386/i386-protos.h | 1 +
gcc/config/i386/i386.c | 151 +++++++++++++++++++--
gcc/config/i386/i386.h | 3 +
gcc/config/i386/i386.md | 9 +-
gcc/config/i386/i386.opt | 6 +-
gcc/doc/extend.texi | 9 ++
gcc/doc/invoke.texi | 13 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-8.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-7.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-1.c | 12 ++
gcc/testsuite/gcc.target/i386/ret-thunk-10.c | 22 +++
gcc/testsuite/gcc.target/i386/ret-thunk-11.c | 22 +++
gcc/testsuite/gcc.target/i386/ret-thunk-12.c | 21 +++
gcc/testsuite/gcc.target/i386/ret-thunk-13.c | 21 +++
gcc/testsuite/gcc.target/i386/ret-thunk-14.c | 21 +++
gcc/testsuite/gcc.target/i386/ret-thunk-15.c | 21 +++
gcc/testsuite/gcc.target/i386/ret-thunk-16.c | 18 +++
gcc/testsuite/gcc.target/i386/ret-thunk-2.c | 12 ++
gcc/testsuite/gcc.target/i386/ret-thunk-3.c | 12 ++
gcc/testsuite/gcc.target/i386/ret-thunk-4.c | 12 ++
gcc/testsuite/gcc.target/i386/ret-thunk-5.c | 14 ++
gcc/testsuite/gcc.target/i386/ret-thunk-6.c | 13 ++
gcc/testsuite/gcc.target/i386/ret-thunk-7.c | 13 ++
gcc/testsuite/gcc.target/i386/ret-thunk-8.c | 14 ++
gcc/testsuite/gcc.target/i386/ret-thunk-9.c | 23 ++++
56 files changed, 479 insertions(+), 50 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-10.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-11.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-12.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-13.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-14.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-15.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-16.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-8.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-9.c
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index bf11cc426f9..fb86f00b3a6 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -306,6 +306,7 @@ extern enum attr_cpu ix86_schedule;
extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
+extern const char * ix86_output_function_return (bool long_p);
extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
machine_mode mode);
extern int ix86_min_insn_size (rtx_insn *);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index d8b44ea510f..836cc50cf67 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -5837,6 +5837,31 @@ ix86_set_indirect_branch_type (tree fndecl)
else
cfun->machine->indirect_branch_type = ix86_indirect_branch;
}
+
+ if (cfun->machine->function_return_type == indirect_branch_unset)
+ {
+ tree attr = lookup_attribute ("function_return",
+ DECL_ATTRIBUTES (fndecl));
+ if (attr != NULL)
+ {
+ tree args = TREE_VALUE (attr);
+ if (args == NULL)
+ gcc_unreachable ();
+ tree cst = TREE_VALUE (args);
+ if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0)
+ cfun->machine->function_return_type = indirect_branch_keep;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0)
+ cfun->machine->function_return_type = indirect_branch_thunk;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0)
+ cfun->machine->function_return_type = indirect_branch_thunk_inline;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0)
+ cfun->machine->function_return_type = indirect_branch_thunk_extern;
+ else
+ gcc_unreachable ();
+ }
+ else
+ cfun->machine->function_return_type = ix86_function_return;
+ }
}
/* Establish appropriate back-end context for processing the function
@@ -10709,8 +10734,12 @@ static int indirect_thunks_bnd_used;
/* Fills in the label name that should be used for the indirect thunk. */
static void
-indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
+indirect_thunk_name (char name[32], int regno, bool need_bnd_p,
+ bool ret_p)
{
+ if (regno >= 0 && ret_p)
+ gcc_unreachable ();
+
if (USE_HIDDEN_LINKONCE)
{
const char *bnd = need_bnd_p ? "_bnd" : "";
@@ -10725,7 +10754,10 @@ indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
bnd, reg_prefix, reg_names[regno]);
}
else
- sprintf (name, "__x86_indirect_thunk%s", bnd);
+ {
+ const char *ret = ret_p ? "return" : "indirect";
+ sprintf (name, "__x86_%s_thunk%s", ret, bnd);
+ }
}
else
{
@@ -10738,10 +10770,20 @@ indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
}
else
{
- if (need_bnd_p)
- ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
+ if (ret_p)
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LRTB", 0);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LRT", 0);
+ }
else
- ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
+ }
}
}
}
@@ -10836,7 +10878,7 @@ output_indirect_thunk_function (bool need_bnd_p, int regno)
tree decl;
/* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd. */
- indirect_thunk_name (name, regno, need_bnd_p);
+ indirect_thunk_name (name, regno, need_bnd_p, false);
decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
get_identifier (name),
build_function_type_list (void_type_node, NULL_TREE));
@@ -10879,6 +10921,35 @@ output_indirect_thunk_function (bool need_bnd_p, int regno)
ASM_OUTPUT_LABEL (asm_out_file, name);
}
+ if (regno < 0)
+ {
+ /* Create alias for __x86.return_thunk/__x86.return_thunk_bnd. */
+ char alias[32];
+
+ indirect_thunk_name (alias, regno, need_bnd_p, true);
+ ASM_OUTPUT_DEF (asm_out_file, alias, name);
+#if TARGET_MACHO
+ if (TARGET_MACHO)
+ {
+ fputs ("\t.weak_definition\t", asm_out_file);
+ assemble_name (asm_out_file, alias);
+ fputs ("\n\t.private_extern\t", asm_out_file);
+ assemble_name (asm_out_file, alias);
+ putc ('\n', asm_out_file);
+ }
+#else
+ if (USE_HIDDEN_LINKONCE)
+ {
+ fputs ("\t.globl\t", asm_out_file);
+ assemble_name (asm_out_file, alias);
+ putc ('\n', asm_out_file);
+ fputs ("\t.hidden\t", asm_out_file);
+ assemble_name (asm_out_file, alias);
+ putc ('\n', asm_out_file);
+ }
+#endif
+ }
+
DECL_INITIAL (decl) = make_node (BLOCK);
current_function_decl = decl;
allocate_struct_function (decl, false);
@@ -28464,7 +28535,7 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
else
indirect_thunks_used |= 1 << i;
}
- indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false);
thunk_name = thunk_name_buf;
}
else
@@ -28573,7 +28644,7 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
else
indirect_thunk_needed = true;
}
- indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false);
thunk_name = thunk_name_buf;
}
else
@@ -28708,6 +28779,46 @@ ix86_output_indirect_jmp (rtx call_op, bool ret_p)
return "%!jmp\t%A0";
}
+/* Output function return. CALL_OP is the jump target. Add a REP
+ prefix to RET if LONG_P is true and function return is kept. */
+
+const char *
+ix86_output_function_return (bool long_p)
+{
+ if (cfun->machine->function_return_type != indirect_branch_keep)
+ {
+ char thunk_name[32];
+ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+
+ if (cfun->machine->function_return_type
+ != indirect_branch_thunk_inline)
+ {
+ bool need_thunk = (cfun->machine->function_return_type
+ == indirect_branch_thunk);
+ indirect_thunk_name (thunk_name, -1, need_bnd_p, true);
+ if (need_bnd_p)
+ {
+ indirect_thunk_bnd_needed |= need_thunk;
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ }
+ else
+ {
+ indirect_thunk_needed |= need_thunk;
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ }
+ else
+ output_indirect_thunk (need_bnd_p, -1);
+
+ return "";
+ }
+
+ if (!long_p || ix86_bnd_prefixed_insn_p (current_output_insn))
+ return "%!ret";
+
+ return "rep%; ret";
+}
+
/* Output the assembly for a call instruction. */
const char *
@@ -41018,6 +41129,28 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
}
}
+ if (is_attribute_p ("function_return", name))
+ {
+ tree cst = TREE_VALUE (args);
+ if (TREE_CODE (cst) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qE attribute requires a string constant argument",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
+ {
+ warning (OPT_Wattributes,
+ "argument to %qE attribute is not "
+ "(keep|thunk|thunk-inline|thunk-extern)", name);
+ *no_add_attrs = true;
+ }
+ }
+
return NULL_TREE;
}
@@ -45466,6 +45599,8 @@ static const struct attribute_spec ix86_attribute_table[] =
ix86_handle_fndecl_attribute, NULL },
{ "indirect_branch", 1, 1, true, false, false, false,
ix86_handle_fndecl_attribute, NULL },
+ { "function_return", 1, 1, true, false, false, false,
+ ix86_handle_fndecl_attribute, NULL },
/* End element. */
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 3b939086112..bc4bc9a7a48 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2577,6 +2577,9 @@ struct GTY(()) machine_function {
"indirect_jump" or "tablejump". */
BOOL_BITFIELD has_local_indirect_jump : 1;
+ /* How to generate function return. */
+ ENUM_BITFIELD(indirect_branch) function_return_type : 3;
+
/* If true, the current function is a function specified with
the "interrupt" or "no_caller_saved_registers" attribute. */
BOOL_BITFIELD no_caller_saved_registers : 1;
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 1ba144da87e..7e21c486d19 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -13058,7 +13058,7 @@
(define_insn "simple_return_internal"
[(simple_return)]
"reload_completed"
- "%!ret"
+ "* return ix86_output_function_return (false);"
[(set_attr "length" "1")
(set_attr "atom_unit" "jeu")
(set_attr "length_immediate" "0")
@@ -13080,12 +13080,7 @@
[(simple_return)
(unspec [(const_int 0)] UNSPEC_REP)]
"reload_completed"
-{
- if (ix86_bnd_prefixed_insn_p (insn))
- return "%!ret";
-
- return "rep%; ret";
-}
+ "* return ix86_output_function_return (true);"
[(set_attr "length" "2")
(set_attr "atom_unit" "jeu")
(set_attr "length_immediate" "0")
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 59e5cc8e7e4..7b17773592b 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -1026,9 +1026,13 @@ mindirect-branch=
Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep)
Convert indirect call and jump to call and return thunks.
+mfunction-return=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_function_return) Init(indirect_branch_keep)
+Convert function return to call and return thunk.
+
Enum
Name(indirect_branch) Type(enum indirect_branch)
-Known indirect branch choices (for use with the -mindirect-branch= option):
+Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options):
EnumValue
Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ddb6035be96..f120b2a1429 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5764,6 +5764,15 @@ indirect call and jump to inlined call and return thunk.
@samp{thunk-extern} converts indirect call and jump to external call
and return thunk provided in a separate object file.
+@item function_return("@var{choice}")
+@cindex @code{function_return} function attribute, x86
+On x86 targets, the @code{function_return} attribute causes the compiler
+to convert function return with @var{choice}. @samp{keep} keeps function
+return unmodified. @samp{thunk} converts function return to call and
+return thunk. @samp{thunk-inline} converts function return to inlined
+call and return thunk. @samp{thunk-extern} converts function return to
+external call and return thunk provided in a separate object file.
+
@item nocf_check
@cindex @code{nocf_check} function attribute
The @code{nocf_check} attribute on a function is used to inform the
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 0d685c3576b..df945989fe8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1230,7 +1230,7 @@ See RS/6000 and PowerPC Options.
-mstack-protector-guard-offset=@var{offset} @gol
-mstack-protector-guard-symbol=@var{symbol} -mmitigate-rop @gol
-mgeneral-regs-only -mcall-ms2sysv-xlogues @gol
--mindirect-branch=@var{choice}}
+-mindirect-branch=@var{choice} -mfunction-return==@var{choice}}
@emph{x86 Windows Options}
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
@@ -26850,6 +26850,17 @@ to external call and return thunk provided in a separate object file.
You can control this behavior for a specific function by using the
function attribute @code{indirect_branch}. @xref{Function Attributes}.
+@item -mfunction-return=@var{choice}
+@opindex -mfunction-return
+Convert function return with @var{choice}. The default is @samp{keep},
+which keeps function return unmodified. @samp{thunk} converts function
+return to call and return thunk. @samp{thunk-inline} converts function
+return to inlined call and return thunk. @samp{thunk-extern} converts
+function return to external call and return thunk provided in a separate
+object file. You can control this behavior for a specific function by
+using the function attribute @code{function_return}.
+@xref{Function Attributes}.
+
@end table
These @samp{-m} switches are supported in addition to the above
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
index d1d2ee78797..527e447aea5 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
index 08646c6b823..7dbc7607e2e 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
index af244de2238..c085b21582c 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
index b8aedd5a4e6..f92968bf616 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
index 6ffb9235f94..4d19fac21d8 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
index e6d9d148cd2..5cbdd85303e 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
index d892d8f5992..c59dd049883 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
index 24188d0b62d..61b9c80de33 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
index 03184b90cda..dcd2381c514 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
index af167840b81..21b69728796 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
index 146124894a0..0bd6aab2fd6 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
index 568327cd8e7..99226dbdd1f 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
index bd8a99e7828..aceb4041275 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
index 356015c9799..43d19bbe876 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
index 6960fa0bbfb..c246f974610 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
index febf32d76ea..bbfaf6ba7e7 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { ! x32 } } } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
void (*dispatch) (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
index 319ba30b78b..6c82a236c1b 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { ! x32 } } } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
void (*dispatch) (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
index 9168b3146f5..299940de399 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
void bar (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
index d3b36d44c7c..77ee84b938b 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
void bar (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
index 9e50b282f77..782960375af 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
index f897d1c0497..240c15be8a6 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
index 25905cd0016..6e49707875e 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
index a7fa12183af..e1d8891380c 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
index 48a49760be6..6ad05b70604 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
index a1c662f7d23..cfb0894ae49 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
index 40a665ea640..205b9b405bf 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
index 3ace8d1b031..efa0096e1e0 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
index 6c97b96f1f2..775d0b8c53e 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
index 8f6759cbf06..788271f049f 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
index b07d08cab0f..ef8a2c746a7 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
index 10794886b1b..848ceefca02 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
index a26ec4b06ed..64608100782 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
index 77253af17c6..3c2758360f5 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-1.c b/gcc/testsuite/gcc.target/i386/ret-thunk-1.c
new file mode 100644
index 00000000000..42919722e13
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-1.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
new file mode 100644
index 00000000000..b5164bfc5ad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
+
+extern void (*bar) (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-times {\tpause} 2 } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
new file mode 100644
index 00000000000..a26ac963ea5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */
+
+extern void (*bar) (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
new file mode 100644
index 00000000000..d0106da38ee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+
+extern void (*bar) (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
new file mode 100644
index 00000000000..185ad366190
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+
+extern void (*bar) (void);
+extern int foo (void) __attribute__ ((function_return("thunk")));
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-times {\tpause} 2 } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 3 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 3 } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
new file mode 100644
index 00000000000..cce8b20b5f1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+
+extern void (*bar) (void);
+
+__attribute__ ((function_return("thunk-inline")))
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
new file mode 100644
index 00000000000..0316d301d9c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */
+
+extern void (*bar) (void);
+
+__attribute__ ((function_return("thunk-extern"), indirect_branch("thunk")))
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-16.c b/gcc/testsuite/gcc.target/i386/ret-thunk-16.c
new file mode 100644
index 00000000000..9de35bcbd27
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-16.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk-extern -fno-pic" } */
+
+extern void (*bar) (void);
+
+__attribute__ ((function_return("keep"), indirect_branch("keep")))
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\tpause} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-2.c b/gcc/testsuite/gcc.target/i386/ret-thunk-2.c
new file mode 100644
index 00000000000..a35bedc425d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-2.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-3.c b/gcc/testsuite/gcc.target/i386/ret-thunk-3.c
new file mode 100644
index 00000000000..b4bbf27f7c5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-3.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\tpause} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-4.c b/gcc/testsuite/gcc.target/i386/ret-thunk-4.c
new file mode 100644
index 00000000000..55223d0a41c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-4.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\tpause} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-5.c b/gcc/testsuite/gcc.target/i386/ret-thunk-5.c
new file mode 100644
index 00000000000..036787b2e01
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-5.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep" } */
+
+extern void foo (void) __attribute__ ((function_return("thunk")));
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-6.c b/gcc/testsuite/gcc.target/i386/ret-thunk-6.c
new file mode 100644
index 00000000000..29bb20b7f18
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-6.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep" } */
+
+__attribute__ ((function_return("thunk-inline")))
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-7.c b/gcc/testsuite/gcc.target/i386/ret-thunk-7.c
new file mode 100644
index 00000000000..39925c6201b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-7.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep" } */
+
+__attribute__ ((function_return("thunk-extern")))
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\tpause} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-8.c b/gcc/testsuite/gcc.target/i386/ret-thunk-8.c
new file mode 100644
index 00000000000..1e2e1e047a3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-8.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline" } */
+
+extern void foo (void) __attribute__ ((function_return("keep")));
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\tpause} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
new file mode 100644
index 00000000000..92298c362ec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
+
+extern void (*bar) (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not "__x86_return_thunk:" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk:" } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times {\tpause} 2 { target { x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 2/5] x86: Add -mfunction-return=
2018-01-14 3:37 ` [PATCH 2/5] x86: Add -mfunction-return= H.J. Lu
@ 2018-01-14 10:51 ` Jan Hubicka
2018-01-14 11:33 ` H.J. Lu
0 siblings, 1 reply; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 10:51 UTC (permalink / raw)
To: H.J. Lu; +Cc: gcc-patches
> Add -mfunction-return= option to convert function return to call and
> return thunks. The default is 'keep', which keeps function return
> unmodified. 'thunk' converts function return to call and return thunk.
> 'thunk-inline' converts function return to inlined call and return thunk.
> 'thunk-extern' converts function return to external call and return
> thunk provided in a separate object file. You can control this behavior
> for a specific function by using the function attribute function_return.
>
> Function return thunk is the same as memory thunk for -mindirect-branch=
> where the return address is at the top of the stack:
>
> __x86_return_thunk:
> call L2
> L1:
> pause
> jmp L1
> L2:
> lea 8(%rsp), %rsp|lea 4(%esp), %esp
> ret
>
> and function return becomes
>
> jmp __x86_return_thunk
>
> -mindirect-branch= tests are updated with -mfunction-return=keep to
> avoid false test failures when -mfunction-return=thunk is added to
> RUNTESTFLAGS for "make check".
>
> gcc/
>
> * config/i386/i386-protos.h (ix86_output_function_return): New.
> * config/i386/i386.c (ix86_set_indirect_branch_type): Also
> set function_return_type.
> (indirect_thunk_name): Add ret_p to indicate thunk for function
> return.
> (output_indirect_thunk_function): Pass false to
> indirect_thunk_name.
> (ix86_output_indirect_branch): Likewise.
> (output_indirect_thunk_function): Create alias for function
> return thunk if regno < 0.
> (ix86_output_function_return): New function.
> (ix86_handle_fndecl_attribute): Handle function_return.
> (ix86_attribute_table): Add function_return.
> * config/i386/i386.h (machine_function): Add
> function_return_type.
> * config/i386/i386.md (simple_return_internal): Use
> ix86_output_function_return.
> (simple_return_internal_long): Likewise.
> * config/i386/i386.opt (mfunction-return=): New option.
> (indirect_branch): Mention -mfunction-return=.
> * doc/extend.texi: Document function_return function attribute.
> * doc/invoke.texi: Document -mfunction-return= option.
The implementation is reasonable, but I still do not quite understand
if we really need this change. Celarly killing every return is going
to have significant impact and it is apparently not used by Linux kernel.
What is the main motivation for it?
Honza
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 2/5] x86: Add -mfunction-return=
2018-01-14 10:51 ` Jan Hubicka
@ 2018-01-14 11:33 ` H.J. Lu
2018-01-14 12:57 ` H.J. Lu
0 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 11:33 UTC (permalink / raw)
To: Jan Hubicka, Woodhouse, David; +Cc: GCC Patches
On Sun, Jan 14, 2018 at 2:48 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Add -mfunction-return= option to convert function return to call and
>> return thunks. The default is 'keep', which keeps function return
>> unmodified. 'thunk' converts function return to call and return thunk.
>> 'thunk-inline' converts function return to inlined call and return thunk.
>> 'thunk-extern' converts function return to external call and return
>> thunk provided in a separate object file. You can control this behavior
>> for a specific function by using the function attribute function_return.
>>
>> Function return thunk is the same as memory thunk for -mindirect-branch=
>> where the return address is at the top of the stack:
>>
>> __x86_return_thunk:
>> call L2
>> L1:
>> pause
>> jmp L1
>> L2:
>> lea 8(%rsp), %rsp|lea 4(%esp), %esp
>> ret
>>
>> and function return becomes
>>
>> jmp __x86_return_thunk
>>
>> -mindirect-branch= tests are updated with -mfunction-return=keep to
>> avoid false test failures when -mfunction-return=thunk is added to
>> RUNTESTFLAGS for "make check".
>>
>> gcc/
>>
>> * config/i386/i386-protos.h (ix86_output_function_return): New.
>> * config/i386/i386.c (ix86_set_indirect_branch_type): Also
>> set function_return_type.
>> (indirect_thunk_name): Add ret_p to indicate thunk for function
>> return.
>> (output_indirect_thunk_function): Pass false to
>> indirect_thunk_name.
>> (ix86_output_indirect_branch): Likewise.
>> (output_indirect_thunk_function): Create alias for function
>> return thunk if regno < 0.
>> (ix86_output_function_return): New function.
>> (ix86_handle_fndecl_attribute): Handle function_return.
>> (ix86_attribute_table): Add function_return.
>> * config/i386/i386.h (machine_function): Add
>> function_return_type.
>> * config/i386/i386.md (simple_return_internal): Use
>> ix86_output_function_return.
>> (simple_return_internal_long): Likewise.
>> * config/i386/i386.opt (mfunction-return=): New option.
>> (indirect_branch): Mention -mfunction-return=.
>> * doc/extend.texi: Document function_return function attribute.
>> * doc/invoke.texi: Document -mfunction-return= option.
>
> The implementation is reasonable, but I still do not quite understand
> if we really need this change. Celarly killing every return is going
> to have significant impact and it is apparently not used by Linux kernel.
> What is the main motivation for it?
>
> Honza
See:
https://gcc.gnu.org/ml/gcc-patches/2018-01/msg00540.html
I will check it in.
--
H.J.
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 2/5] x86: Add -mfunction-return=
2018-01-14 11:33 ` H.J. Lu
@ 2018-01-14 12:57 ` H.J. Lu
2018-01-14 15:12 ` H.J. Lu
0 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 12:57 UTC (permalink / raw)
To: Jan Hubicka, Woodhouse, David; +Cc: GCC Patches
[-- Attachment #1: Type: text/plain, Size: 2845 bytes --]
On Sun, Jan 14, 2018 at 3:31 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Sun, Jan 14, 2018 at 2:48 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> Add -mfunction-return= option to convert function return to call and
>>> return thunks. The default is 'keep', which keeps function return
>>> unmodified. 'thunk' converts function return to call and return thunk.
>>> 'thunk-inline' converts function return to inlined call and return thunk.
>>> 'thunk-extern' converts function return to external call and return
>>> thunk provided in a separate object file. You can control this behavior
>>> for a specific function by using the function attribute function_return.
>>>
>>> Function return thunk is the same as memory thunk for -mindirect-branch=
>>> where the return address is at the top of the stack:
>>>
>>> __x86_return_thunk:
>>> call L2
>>> L1:
>>> pause
>>> jmp L1
>>> L2:
>>> lea 8(%rsp), %rsp|lea 4(%esp), %esp
>>> ret
>>>
>>> and function return becomes
>>>
>>> jmp __x86_return_thunk
>>>
>>> -mindirect-branch= tests are updated with -mfunction-return=keep to
>>> avoid false test failures when -mfunction-return=thunk is added to
>>> RUNTESTFLAGS for "make check".
>>>
>>> gcc/
>>>
>>> * config/i386/i386-protos.h (ix86_output_function_return): New.
>>> * config/i386/i386.c (ix86_set_indirect_branch_type): Also
>>> set function_return_type.
>>> (indirect_thunk_name): Add ret_p to indicate thunk for function
>>> return.
>>> (output_indirect_thunk_function): Pass false to
>>> indirect_thunk_name.
>>> (ix86_output_indirect_branch): Likewise.
>>> (output_indirect_thunk_function): Create alias for function
>>> return thunk if regno < 0.
>>> (ix86_output_function_return): New function.
>>> (ix86_handle_fndecl_attribute): Handle function_return.
>>> (ix86_attribute_table): Add function_return.
>>> * config/i386/i386.h (machine_function): Add
>>> function_return_type.
>>> * config/i386/i386.md (simple_return_internal): Use
>>> ix86_output_function_return.
>>> (simple_return_internal_long): Likewise.
>>> * config/i386/i386.opt (mfunction-return=): New option.
>>> (indirect_branch): Mention -mfunction-return=.
>>> * doc/extend.texi: Document function_return function attribute.
>>> * doc/invoke.texi: Document -mfunction-return= option.
>>
>> The implementation is reasonable, but I still do not quite understand
>> if we really need this change. Celarly killing every return is going
>> to have significant impact and it is apparently not used by Linux kernel.
>> What is the main motivation for it?
>>
>> Honza
>
> See:
>
> https://gcc.gnu.org/ml/gcc-patches/2018-01/msg00540.html
>
> I will check it in.
>
Here is the patch I am checking in.
--
H.J.
[-- Attachment #2: 0002-x86-Add-mfunction-return.patch --]
[-- Type: text/x-patch, Size: 53752 bytes --]
From 2f05046a78afb173f301298df0a9a44b6fb38969 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 6 Jan 2018 22:29:56 -0800
Subject: [PATCH 2/6] x86: Add -mfunction-return=
Add -mfunction-return= option to convert function return to call and
return thunks. The default is 'keep', which keeps function return
unmodified. 'thunk' converts function return to call and return thunk.
'thunk-inline' converts function return to inlined call and return thunk.
'thunk-extern' converts function return to external call and return
thunk provided in a separate object file. You can control this behavior
for a specific function by using the function attribute function_return.
Function return thunk is the same as memory thunk for -mindirect-branch=
where the return address is at the top of the stack:
__x86_return_thunk:
call L2
L1:
pause
lfence
jmp L1
L2:
lea 8(%rsp), %rsp|lea 4(%esp), %esp
ret
and function return becomes
jmp __x86_return_thunk
-mindirect-branch= tests are updated with -mfunction-return=keep to
avoid false test failures when -mfunction-return=thunk is added to
RUNTESTFLAGS for "make check".
gcc/
* config/i386/i386-protos.h (ix86_output_function_return): New.
* config/i386/i386.c (ix86_set_indirect_branch_type): Also
set function_return_type.
(indirect_thunk_name): Add ret_p to indicate thunk for function
return.
(output_indirect_thunk_function): Pass false to
indirect_thunk_name.
(ix86_output_indirect_branch): Likewise.
(output_indirect_thunk_function): Create alias for function
return thunk if regno < 0.
(ix86_output_function_return): New function.
(ix86_handle_fndecl_attribute): Handle function_return.
(ix86_attribute_table): Add function_return.
* config/i386/i386.h (machine_function): Add
function_return_type.
* config/i386/i386.md (simple_return_internal): Use
ix86_output_function_return.
(simple_return_internal_long): Likewise.
* config/i386/i386.opt (mfunction-return=): New option.
(indirect_branch): Mention -mfunction-return=.
* doc/extend.texi: Document function_return function attribute.
* doc/invoke.texi: Document -mfunction-return= option.
gcc/testsuite/
* gcc.target/i386/indirect-thunk-1.c (dg-options): Add
-mfunction-return=keep.
* gcc.target/i386/indirect-thunk-2.c: Likewise.
* gcc.target/i386/indirect-thunk-3.c: Likewise.
* gcc.target/i386/indirect-thunk-4.c: Likewise.
* gcc.target/i386/indirect-thunk-5.c: Likewise.
* gcc.target/i386/indirect-thunk-6.c: Likewise.
* gcc.target/i386/indirect-thunk-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
* gcc.target/i386/ret-thunk-1.c: New test.
* gcc.target/i386/ret-thunk-10.c: Likewise.
* gcc.target/i386/ret-thunk-11.c: Likewise.
* gcc.target/i386/ret-thunk-12.c: Likewise.
* gcc.target/i386/ret-thunk-13.c: Likewise.
* gcc.target/i386/ret-thunk-14.c: Likewise.
* gcc.target/i386/ret-thunk-15.c: Likewise.
* gcc.target/i386/ret-thunk-16.c: Likewise.
* gcc.target/i386/ret-thunk-2.c: Likewise.
* gcc.target/i386/ret-thunk-3.c: Likewise.
* gcc.target/i386/ret-thunk-4.c: Likewise.
* gcc.target/i386/ret-thunk-5.c: Likewise.
* gcc.target/i386/ret-thunk-6.c: Likewise.
* gcc.target/i386/ret-thunk-7.c: Likewise.
* gcc.target/i386/ret-thunk-8.c: Likewise.
* gcc.target/i386/ret-thunk-9.c: Likewise.
---
gcc/config/i386/i386-protos.h | 1 +
gcc/config/i386/i386.c | 151 +++++++++++++++++++--
gcc/config/i386/i386.h | 3 +
gcc/config/i386/i386.md | 9 +-
gcc/config/i386/i386.opt | 6 +-
gcc/doc/extend.texi | 9 ++
gcc/doc/invoke.texi | 13 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-8.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-7.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-1.c | 13 ++
gcc/testsuite/gcc.target/i386/ret-thunk-10.c | 23 ++++
gcc/testsuite/gcc.target/i386/ret-thunk-11.c | 23 ++++
gcc/testsuite/gcc.target/i386/ret-thunk-12.c | 22 +++
gcc/testsuite/gcc.target/i386/ret-thunk-13.c | 22 +++
gcc/testsuite/gcc.target/i386/ret-thunk-14.c | 22 +++
gcc/testsuite/gcc.target/i386/ret-thunk-15.c | 22 +++
gcc/testsuite/gcc.target/i386/ret-thunk-16.c | 18 +++
gcc/testsuite/gcc.target/i386/ret-thunk-2.c | 13 ++
gcc/testsuite/gcc.target/i386/ret-thunk-3.c | 12 ++
gcc/testsuite/gcc.target/i386/ret-thunk-4.c | 12 ++
gcc/testsuite/gcc.target/i386/ret-thunk-5.c | 15 ++
gcc/testsuite/gcc.target/i386/ret-thunk-6.c | 14 ++
gcc/testsuite/gcc.target/i386/ret-thunk-7.c | 13 ++
gcc/testsuite/gcc.target/i386/ret-thunk-8.c | 14 ++
gcc/testsuite/gcc.target/i386/ret-thunk-9.c | 25 ++++
56 files changed, 491 insertions(+), 50 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-10.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-11.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-12.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-13.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-14.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-15.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-16.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-8.c
create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-9.c
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index bf11cc426f9..fb86f00b3a6 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -306,6 +306,7 @@ extern enum attr_cpu ix86_schedule;
extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
+extern const char * ix86_output_function_return (bool long_p);
extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
machine_mode mode);
extern int ix86_min_insn_size (rtx_insn *);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 43524f28d29..98c789094a4 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -5837,6 +5837,31 @@ ix86_set_indirect_branch_type (tree fndecl)
else
cfun->machine->indirect_branch_type = ix86_indirect_branch;
}
+
+ if (cfun->machine->function_return_type == indirect_branch_unset)
+ {
+ tree attr = lookup_attribute ("function_return",
+ DECL_ATTRIBUTES (fndecl));
+ if (attr != NULL)
+ {
+ tree args = TREE_VALUE (attr);
+ if (args == NULL)
+ gcc_unreachable ();
+ tree cst = TREE_VALUE (args);
+ if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0)
+ cfun->machine->function_return_type = indirect_branch_keep;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0)
+ cfun->machine->function_return_type = indirect_branch_thunk;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0)
+ cfun->machine->function_return_type = indirect_branch_thunk_inline;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0)
+ cfun->machine->function_return_type = indirect_branch_thunk_extern;
+ else
+ gcc_unreachable ();
+ }
+ else
+ cfun->machine->function_return_type = ix86_function_return;
+ }
}
/* Establish appropriate back-end context for processing the function
@@ -10709,8 +10734,12 @@ static int indirect_thunks_bnd_used;
/* Fills in the label name that should be used for the indirect thunk. */
static void
-indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
+indirect_thunk_name (char name[32], int regno, bool need_bnd_p,
+ bool ret_p)
{
+ if (regno >= 0 && ret_p)
+ gcc_unreachable ();
+
if (USE_HIDDEN_LINKONCE)
{
const char *bnd = need_bnd_p ? "_bnd" : "";
@@ -10725,7 +10754,10 @@ indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
bnd, reg_prefix, reg_names[regno]);
}
else
- sprintf (name, "__x86_indirect_thunk%s", bnd);
+ {
+ const char *ret = ret_p ? "return" : "indirect";
+ sprintf (name, "__x86_%s_thunk%s", ret, bnd);
+ }
}
else
{
@@ -10738,10 +10770,20 @@ indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
}
else
{
- if (need_bnd_p)
- ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
+ if (ret_p)
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LRTB", 0);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LRT", 0);
+ }
else
- ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
+ }
}
}
}
@@ -10836,7 +10878,7 @@ output_indirect_thunk_function (bool need_bnd_p, int regno)
tree decl;
/* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd. */
- indirect_thunk_name (name, regno, need_bnd_p);
+ indirect_thunk_name (name, regno, need_bnd_p, false);
decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
get_identifier (name),
build_function_type_list (void_type_node, NULL_TREE));
@@ -10879,6 +10921,35 @@ output_indirect_thunk_function (bool need_bnd_p, int regno)
ASM_OUTPUT_LABEL (asm_out_file, name);
}
+ if (regno < 0)
+ {
+ /* Create alias for __x86.return_thunk/__x86.return_thunk_bnd. */
+ char alias[32];
+
+ indirect_thunk_name (alias, regno, need_bnd_p, true);
+ ASM_OUTPUT_DEF (asm_out_file, alias, name);
+#if TARGET_MACHO
+ if (TARGET_MACHO)
+ {
+ fputs ("\t.weak_definition\t", asm_out_file);
+ assemble_name (asm_out_file, alias);
+ fputs ("\n\t.private_extern\t", asm_out_file);
+ assemble_name (asm_out_file, alias);
+ putc ('\n', asm_out_file);
+ }
+#else
+ if (USE_HIDDEN_LINKONCE)
+ {
+ fputs ("\t.globl\t", asm_out_file);
+ assemble_name (asm_out_file, alias);
+ putc ('\n', asm_out_file);
+ fputs ("\t.hidden\t", asm_out_file);
+ assemble_name (asm_out_file, alias);
+ putc ('\n', asm_out_file);
+ }
+#endif
+ }
+
DECL_INITIAL (decl) = make_node (BLOCK);
current_function_decl = decl;
allocate_struct_function (decl, false);
@@ -28464,7 +28535,7 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
else
indirect_thunks_used |= 1 << i;
}
- indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false);
thunk_name = thunk_name_buf;
}
else
@@ -28573,7 +28644,7 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
else
indirect_thunk_needed = true;
}
- indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false);
thunk_name = thunk_name_buf;
}
else
@@ -28708,6 +28779,46 @@ ix86_output_indirect_jmp (rtx call_op, bool ret_p)
return "%!jmp\t%A0";
}
+/* Output function return. CALL_OP is the jump target. Add a REP
+ prefix to RET if LONG_P is true and function return is kept. */
+
+const char *
+ix86_output_function_return (bool long_p)
+{
+ if (cfun->machine->function_return_type != indirect_branch_keep)
+ {
+ char thunk_name[32];
+ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+
+ if (cfun->machine->function_return_type
+ != indirect_branch_thunk_inline)
+ {
+ bool need_thunk = (cfun->machine->function_return_type
+ == indirect_branch_thunk);
+ indirect_thunk_name (thunk_name, -1, need_bnd_p, true);
+ if (need_bnd_p)
+ {
+ indirect_thunk_bnd_needed |= need_thunk;
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ }
+ else
+ {
+ indirect_thunk_needed |= need_thunk;
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ }
+ else
+ output_indirect_thunk (need_bnd_p, -1);
+
+ return "";
+ }
+
+ if (!long_p || ix86_bnd_prefixed_insn_p (current_output_insn))
+ return "%!ret";
+
+ return "rep%; ret";
+}
+
/* Output the assembly for a call instruction. */
const char *
@@ -41018,6 +41129,28 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
}
}
+ if (is_attribute_p ("function_return", name))
+ {
+ tree cst = TREE_VALUE (args);
+ if (TREE_CODE (cst) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qE attribute requires a string constant argument",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
+ {
+ warning (OPT_Wattributes,
+ "argument to %qE attribute is not "
+ "(keep|thunk|thunk-inline|thunk-extern)", name);
+ *no_add_attrs = true;
+ }
+ }
+
return NULL_TREE;
}
@@ -45466,6 +45599,8 @@ static const struct attribute_spec ix86_attribute_table[] =
ix86_handle_fndecl_attribute, NULL },
{ "indirect_branch", 1, 1, true, false, false, false,
ix86_handle_fndecl_attribute, NULL },
+ { "function_return", 1, 1, true, false, false, false,
+ ix86_handle_fndecl_attribute, NULL },
/* End element. */
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 3b939086112..bc4bc9a7a48 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2577,6 +2577,9 @@ struct GTY(()) machine_function {
"indirect_jump" or "tablejump". */
BOOL_BITFIELD has_local_indirect_jump : 1;
+ /* How to generate function return. */
+ ENUM_BITFIELD(indirect_branch) function_return_type : 3;
+
/* If true, the current function is a function specified with
the "interrupt" or "no_caller_saved_registers" attribute. */
BOOL_BITFIELD no_caller_saved_registers : 1;
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 1ba144da87e..7e21c486d19 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -13058,7 +13058,7 @@
(define_insn "simple_return_internal"
[(simple_return)]
"reload_completed"
- "%!ret"
+ "* return ix86_output_function_return (false);"
[(set_attr "length" "1")
(set_attr "atom_unit" "jeu")
(set_attr "length_immediate" "0")
@@ -13080,12 +13080,7 @@
[(simple_return)
(unspec [(const_int 0)] UNSPEC_REP)]
"reload_completed"
-{
- if (ix86_bnd_prefixed_insn_p (insn))
- return "%!ret";
-
- return "rep%; ret";
-}
+ "* return ix86_output_function_return (true);"
[(set_attr "length" "2")
(set_attr "atom_unit" "jeu")
(set_attr "length_immediate" "0")
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 59e5cc8e7e4..7b17773592b 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -1026,9 +1026,13 @@ mindirect-branch=
Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep)
Convert indirect call and jump to call and return thunks.
+mfunction-return=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_function_return) Init(indirect_branch_keep)
+Convert function return to call and return thunk.
+
Enum
Name(indirect_branch) Type(enum indirect_branch)
-Known indirect branch choices (for use with the -mindirect-branch= option):
+Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options):
EnumValue
Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ddb6035be96..f120b2a1429 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5764,6 +5764,15 @@ indirect call and jump to inlined call and return thunk.
@samp{thunk-extern} converts indirect call and jump to external call
and return thunk provided in a separate object file.
+@item function_return("@var{choice}")
+@cindex @code{function_return} function attribute, x86
+On x86 targets, the @code{function_return} attribute causes the compiler
+to convert function return with @var{choice}. @samp{keep} keeps function
+return unmodified. @samp{thunk} converts function return to call and
+return thunk. @samp{thunk-inline} converts function return to inlined
+call and return thunk. @samp{thunk-extern} converts function return to
+external call and return thunk provided in a separate object file.
+
@item nocf_check
@cindex @code{nocf_check} function attribute
The @code{nocf_check} attribute on a function is used to inform the
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 0d685c3576b..df945989fe8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1230,7 +1230,7 @@ See RS/6000 and PowerPC Options.
-mstack-protector-guard-offset=@var{offset} @gol
-mstack-protector-guard-symbol=@var{symbol} -mmitigate-rop @gol
-mgeneral-regs-only -mcall-ms2sysv-xlogues @gol
--mindirect-branch=@var{choice}}
+-mindirect-branch=@var{choice} -mfunction-return==@var{choice}}
@emph{x86 Windows Options}
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
@@ -26850,6 +26850,17 @@ to external call and return thunk provided in a separate object file.
You can control this behavior for a specific function by using the
function attribute @code{indirect_branch}. @xref{Function Attributes}.
+@item -mfunction-return=@var{choice}
+@opindex -mfunction-return
+Convert function return with @var{choice}. The default is @samp{keep},
+which keeps function return unmodified. @samp{thunk} converts function
+return to call and return thunk. @samp{thunk-inline} converts function
+return to inlined call and return thunk. @samp{thunk-extern} converts
+function return to external call and return thunk provided in a separate
+object file. You can control this behavior for a specific function by
+using the function attribute @code{function_return}.
+@xref{Function Attributes}.
+
@end table
These @samp{-m} switches are supported in addition to the above
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
index d983e1c3e26..f076155c91a 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
index 58f09b42d8a..d7984f592fe 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
index f20d35c19b6..3257d0a2e16 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
index 0eff8fb658a..7cab2df6474 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
index a25b20dd808..b4836c38d6c 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
index cff114a6c29..1f06bd1af74 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
index afdb6007986..0b3fef86a20 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
index d64d978b699..5f6cfc17b56 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
index 93067454d3d..b256160ec80 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
index 97744d65729..567c95051d6 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
index bfce3ea5cb2..3b662af7d5d 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
index 0833606046b..98785a38248 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
index 2eba0fbd9b2..a498a39e404 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
index f58427eae11..66f295d1eb6 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
index 564ed39547c..d730d31bda1 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
index 50fbee20a5a..aacb814d737 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { ! x32 } } } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
void (*dispatch) (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
index 2976e67adce..7b44dda23df 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { ! x32 } } } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
void (*dispatch) (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
index da4bc98ef23..70b4fb36eea 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
void bar (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
index c64d12ef989..3baf03ee77c 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
void bar (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
index 49f27b49465..637fc3d3f4e 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
index a1e3eb6fc74..ff9efe03fe6 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
index 395634e7e5c..2686a5f2db4 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
index fd3f63379a1..f07f6b214ad 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
index ba2f92b6f34..21740ac5b7f 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
index 0c5a2d472c6..a77c1f470b8 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
index 665252327aa..e64910fd4aa 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
index 68c0ff713b3..365cf2ee226 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
index e2da1fcb683..72646a4960b 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
index 244fec708d6..f48945e3dfc 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
index 107ebe32f54..4b1d558fc4e 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
index 17b04ef2229..0f687c3b027 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
index d9eb11285aa..b27c6fc96a2 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
index d02b1dcb1b9..2c496492eaa 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-1.c b/gcc/testsuite/gcc.target/i386/ret-thunk-1.c
new file mode 100644
index 00000000000..7223f67ba5e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-1.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
new file mode 100644
index 00000000000..1630e2fa2b5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
+
+extern void (*bar) (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-times {\tpause} 2 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 2 } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
new file mode 100644
index 00000000000..876159cf783
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */
+
+extern void (*bar) (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
new file mode 100644
index 00000000000..01b0a02f80b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+
+extern void (*bar) (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
new file mode 100644
index 00000000000..e028c2b6a99
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+
+extern void (*bar) (void);
+extern int foo (void) __attribute__ ((function_return("thunk")));
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-times {\tpause} 2 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 2 } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 3 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 3 } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
new file mode 100644
index 00000000000..c14ee3ae4c0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+
+extern void (*bar) (void);
+
+__attribute__ ((function_return("thunk-inline")))
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
new file mode 100644
index 00000000000..2f21e138ec2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */
+
+extern void (*bar) (void);
+
+__attribute__ ((function_return("thunk-extern"), indirect_branch("thunk")))
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-16.c b/gcc/testsuite/gcc.target/i386/ret-thunk-16.c
new file mode 100644
index 00000000000..a16cad16aaa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-16.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk-extern -fno-pic" } */
+
+extern void (*bar) (void);
+
+__attribute__ ((function_return("keep"), indirect_branch("keep")))
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-2.c b/gcc/testsuite/gcc.target/i386/ret-thunk-2.c
new file mode 100644
index 00000000000..c6659e3ad09
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-3.c b/gcc/testsuite/gcc.target/i386/ret-thunk-3.c
new file mode 100644
index 00000000000..0f7f388f459
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-3.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-4.c b/gcc/testsuite/gcc.target/i386/ret-thunk-4.c
new file mode 100644
index 00000000000..9ae37e835a0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-4.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-5.c b/gcc/testsuite/gcc.target/i386/ret-thunk-5.c
new file mode 100644
index 00000000000..4bd0d2a27bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep" } */
+
+extern void foo (void) __attribute__ ((function_return("thunk")));
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-6.c b/gcc/testsuite/gcc.target/i386/ret-thunk-6.c
new file mode 100644
index 00000000000..053841f6f7d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-6.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep" } */
+
+__attribute__ ((function_return("thunk-inline")))
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-7.c b/gcc/testsuite/gcc.target/i386/ret-thunk-7.c
new file mode 100644
index 00000000000..262e6780112
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-7.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=keep" } */
+
+__attribute__ ((function_return("thunk-extern")))
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-8.c b/gcc/testsuite/gcc.target/i386/ret-thunk-8.c
new file mode 100644
index 00000000000..c1658e96673
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-8.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline" } */
+
+extern void foo (void) __attribute__ ((function_return("keep")));
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
new file mode 100644
index 00000000000..f6ccad98da7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
+
+extern void (*bar) (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */
+/* { dg-final { scan-assembler-not "__x86_return_thunk:" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "__x86_indirect_thunk:" } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times {\tpause} 2 { target { x32 } } } } */
+/* { dg-final { scan-assembler-times {\tlfence} 2 { target { x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 2/5] x86: Add -mfunction-return=
2018-01-14 12:57 ` H.J. Lu
@ 2018-01-14 15:12 ` H.J. Lu
0 siblings, 0 replies; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 15:12 UTC (permalink / raw)
To: Jan Hubicka, Woodhouse, David, Uros Bizjak; +Cc: GCC Patches
On Sun, Jan 14, 2018 at 4:54 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Sun, Jan 14, 2018 at 3:31 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Sun, Jan 14, 2018 at 2:48 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>>> Add -mfunction-return= option to convert function return to call and
>>>> return thunks. The default is 'keep', which keeps function return
>>>> unmodified. 'thunk' converts function return to call and return thunk.
>>>> 'thunk-inline' converts function return to inlined call and return thunk.
>>>> 'thunk-extern' converts function return to external call and return
>>>> thunk provided in a separate object file. You can control this behavior
>>>> for a specific function by using the function attribute function_return.
>>>>
>>>> Function return thunk is the same as memory thunk for -mindirect-branch=
>>>> where the return address is at the top of the stack:
>>>>
>>>> __x86_return_thunk:
>>>> call L2
>>>> L1:
>>>> pause
>>>> jmp L1
>>>> L2:
>>>> lea 8(%rsp), %rsp|lea 4(%esp), %esp
>>>> ret
>>>>
>>>> and function return becomes
>>>>
>>>> jmp __x86_return_thunk
>>>>
>>>> -mindirect-branch= tests are updated with -mfunction-return=keep to
>>>> avoid false test failures when -mfunction-return=thunk is added to
>>>> RUNTESTFLAGS for "make check".
>>>>
>>>> gcc/
>>>>
>>>> * config/i386/i386-protos.h (ix86_output_function_return): New.
>>>> * config/i386/i386.c (ix86_set_indirect_branch_type): Also
>>>> set function_return_type.
>>>> (indirect_thunk_name): Add ret_p to indicate thunk for function
>>>> return.
>>>> (output_indirect_thunk_function): Pass false to
>>>> indirect_thunk_name.
>>>> (ix86_output_indirect_branch): Likewise.
>>>> (output_indirect_thunk_function): Create alias for function
>>>> return thunk if regno < 0.
>>>> (ix86_output_function_return): New function.
>>>> (ix86_handle_fndecl_attribute): Handle function_return.
>>>> (ix86_attribute_table): Add function_return.
>>>> * config/i386/i386.h (machine_function): Add
>>>> function_return_type.
>>>> * config/i386/i386.md (simple_return_internal): Use
>>>> ix86_output_function_return.
>>>> (simple_return_internal_long): Likewise.
>>>> * config/i386/i386.opt (mfunction-return=): New option.
>>>> (indirect_branch): Mention -mfunction-return=.
>>>> * doc/extend.texi: Document function_return function attribute.
>>>> * doc/invoke.texi: Document -mfunction-return= option.
>>>
>>> The implementation is reasonable, but I still do not quite understand
>>> if we really need this change. Celarly killing every return is going
>>> to have significant impact and it is apparently not used by Linux kernel.
>>> What is the main motivation for it?
>>>
>>> Honza
>>
>> See:
>>
>> https://gcc.gnu.org/ml/gcc-patches/2018-01/msg00540.html
>>
>> I will check it in.
>>
>
> Here is the patch I am checking in.
>
>
Here is the backport for GCC 7. OK for gcc-7-branch?
--
H.J.
^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/5] x86: Add -mindirect-branch=
2018-01-14 3:37 [PATCH 0/5] x86: CVE-2017-5715, aka Spectre H.J. Lu
2018-01-14 3:37 ` [PATCH 5/5] x86: Disallow -mindirect-branch=/-mfunction-return= with -mcmodel=large H.J. Lu
2018-01-14 3:37 ` [PATCH 2/5] x86: Add -mfunction-return= H.J. Lu
@ 2018-01-14 3:37 ` H.J. Lu
2018-01-14 10:46 ` Jan Hubicka
2018-01-14 3:37 ` [PATCH 3/5] x86: Add -mindirect-branch-register H.J. Lu
` (2 subsequent siblings)
5 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 3:37 UTC (permalink / raw)
To: gcc-patches
Add -mindirect-branch= option to convert indirect call and jump to call
and return thunks. The default is 'keep', which keeps indirect call and
jump unmodified. 'thunk' converts indirect call and jump to call and
return thunk. 'thunk-inline' converts indirect call and jump to inlined
call and return thunk. 'thunk-extern' converts indirect call and jump to
external call and return thunk provided in a separate object file. You
can control this behavior for a specific function by using the function
attribute indirect_branch.
2 kinds of thunks are geneated. Memory thunk where the function address
is at the top of the stack:
__x86_indirect_thunk:
call L2
L1:
pause
jmp L1
L2:
lea 8(%rsp), %rsp|lea 4(%esp), %esp
ret
Indirect jmp via memory, "jmp mem", is converted to
push memory
jmp __x86_indirect_thunk
Indirect call via memory, "call mem", is converted to
jmp L2
L1:
push [mem]
jmp __x86_indirect_thunk
L2:
call L1
Register thunk where the function address is in a register, reg:
__x86_indirect_thunk_reg:
call L2
L1:
pause
jmp L1
L2:
movq %reg, (%rsp)|movl %reg, (%esp)
ret
where reg is one of (r|e)ax, (r|e)dx, (r|e)cx, (r|e)bx, (r|e)si, (r|e)di,
(r|e)bp, r8, r9, r10, r11, r12, r13, r14 and r15.
Indirect jmp via register, "jmp reg", is converted to
jmp __x86_indirect_thunk_reg
Indirect call via register, "call reg", is converted to
call __x86_indirect_thunk_reg
gcc/
* config/i386/i386-opts.h (indirect_branch): New.
* config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
* config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
with local indirect jump when converting indirect call and jump.
(ix86_set_indirect_branch_type): New.
(ix86_set_current_function): Call ix86_set_indirect_branch_type.
(indirectlabelno): New.
(indirect_thunk_needed): Likewise.
(indirect_thunk_bnd_needed): Likewise.
(indirect_thunks_used): Likewise.
(indirect_thunks_bnd_used): Likewise.
(INDIRECT_LABEL): Likewise.
(indirect_thunk_name): Likewise.
(output_indirect_thunk): Likewise.
(output_indirect_thunk_function): Likewise.
(ix86_output_indirect_branch): Likewise.
(ix86_output_indirect_jmp): Likewise.
(ix86_code_end): Call output_indirect_thunk_function if needed.
(ix86_output_call_insn): Call ix86_output_indirect_branch if
needed.
(ix86_handle_fndecl_attribute): Handle indirect_branch.
(ix86_attribute_table): Add indirect_branch.
* config/i386/i386.h (machine_function): Add indirect_branch_type
and has_local_indirect_jump.
* config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
to true.
(tablejump): Likewise.
(*indirect_jump): Use ix86_output_indirect_jmp.
(*tablejump_1): Likewise.
(simple_return_indirect_internal): Likewise.
* config/i386/i386.opt (mindirect-branch=): New option.
(indirect_branch): New.
(keep): Likewise.
(thunk): Likewise.
(thunk-inline): Likewise.
(thunk-extern): Likewise.
* doc/extend.texi: Document indirect_branch function attribute.
* doc/invoke.texi: Document -mindirect-branch= option.
gcc/testsuite/
* gcc.target/i386/indirect-thunk-1.c: New test.
* gcc.target/i386/indirect-thunk-2.c: Likewise.
* gcc.target/i386/indirect-thunk-3.c: Likewise.
* gcc.target/i386/indirect-thunk-4.c: Likewise.
* gcc.target/i386/indirect-thunk-5.c: Likewise.
* gcc.target/i386/indirect-thunk-6.c: Likewise.
* gcc.target/i386/indirect-thunk-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
---
gcc/config/i386/i386-opts.h | 13 +
gcc/config/i386/i386-protos.h | 1 +
gcc/config/i386/i386.c | 648 ++++++++++++++++++++-
gcc/config/i386/i386.h | 7 +
gcc/config/i386/i386.md | 26 +-
gcc/config/i386/i386.opt | 20 +
gcc/doc/extend.texi | 10 +
gcc/doc/invoke.texi | 14 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 19 +
gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 19 +
gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 20 +
gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 20 +
gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 16 +
gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 17 +
gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 43 ++
.../gcc.target/i386/indirect-thunk-attr-1.c | 22 +
.../gcc.target/i386/indirect-thunk-attr-2.c | 20 +
.../gcc.target/i386/indirect-thunk-attr-3.c | 21 +
.../gcc.target/i386/indirect-thunk-attr-4.c | 20 +
.../gcc.target/i386/indirect-thunk-attr-5.c | 22 +
.../gcc.target/i386/indirect-thunk-attr-6.c | 21 +
.../gcc.target/i386/indirect-thunk-attr-7.c | 44 ++
.../gcc.target/i386/indirect-thunk-attr-8.c | 41 ++
.../gcc.target/i386/indirect-thunk-bnd-1.c | 19 +
.../gcc.target/i386/indirect-thunk-bnd-2.c | 20 +
.../gcc.target/i386/indirect-thunk-bnd-3.c | 18 +
.../gcc.target/i386/indirect-thunk-bnd-4.c | 19 +
.../gcc.target/i386/indirect-thunk-extern-1.c | 19 +
.../gcc.target/i386/indirect-thunk-extern-2.c | 19 +
.../gcc.target/i386/indirect-thunk-extern-3.c | 20 +
.../gcc.target/i386/indirect-thunk-extern-4.c | 20 +
.../gcc.target/i386/indirect-thunk-extern-5.c | 16 +
.../gcc.target/i386/indirect-thunk-extern-6.c | 17 +
.../gcc.target/i386/indirect-thunk-extern-7.c | 43 ++
.../gcc.target/i386/indirect-thunk-inline-1.c | 18 +
.../gcc.target/i386/indirect-thunk-inline-2.c | 18 +
.../gcc.target/i386/indirect-thunk-inline-3.c | 19 +
.../gcc.target/i386/indirect-thunk-inline-4.c | 19 +
.../gcc.target/i386/indirect-thunk-inline-5.c | 15 +
.../gcc.target/i386/indirect-thunk-inline-6.c | 16 +
.../gcc.target/i386/indirect-thunk-inline-7.c | 42 ++
41 files changed, 1462 insertions(+), 19 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h
index f245c1573cf..46366cbfa72 100644
--- a/gcc/config/i386/i386-opts.h
+++ b/gcc/config/i386/i386-opts.h
@@ -106,4 +106,17 @@ enum prefer_vector_width {
PVW_AVX512
};
+/* This is used to mitigate variant #2 of the speculative execution
+ vulnerabilities on x86 processors identified by CVE-2017-5715, aka
+ Spectre. They convert indirect branches and function returns to
+ call and return thunks to avoid speculative execution via indirect
+ call, jmp and ret. */
+enum indirect_branch {
+ indirect_branch_unset = 0,
+ indirect_branch_keep,
+ indirect_branch_thunk,
+ indirect_branch_thunk_inline,
+ indirect_branch_thunk_extern
+};
+
#endif
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 0e49652898c..bf11cc426f9 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -305,6 +305,7 @@ extern enum attr_cpu ix86_schedule;
#endif
extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
+extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
machine_mode mode);
extern int ix86_min_insn_size (rtx_insn *);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 5ee3be386df..d8b44ea510f 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2724,12 +2724,23 @@ make_pass_insert_endbranch (gcc::context *ctxt)
return new pass_insert_endbranch (ctxt);
}
-/* Return true if a red-zone is in use. */
+/* Return true if a red-zone is in use. We can't use red-zone when
+ there are local indirect jumps, like "indirect_jump" or "tablejump",
+ which jumps to another place in the function, since "call" in the
+ indirect thunk pushes the return address onto stack, destroying
+ red-zone.
+
+ TODO: If we can reserve the first 2 WORDs, for PUSH and, another
+ for CALL, in red-zone, we can allow local indirect jumps with
+ indirect thunk. */
bool
ix86_using_red_zone (void)
{
- return TARGET_RED_ZONE && !TARGET_64BIT_MS_ABI;
+ return (TARGET_RED_ZONE
+ && !TARGET_64BIT_MS_ABI
+ && (!cfun->machine->has_local_indirect_jump
+ || cfun->machine->indirect_branch_type == indirect_branch_keep));
}
\f
/* Return a string that documents the current -m options. The caller is
@@ -5797,6 +5808,37 @@ ix86_set_func_type (tree fndecl)
}
}
+/* Set the indirect_branch_type field from the function FNDECL. */
+
+static void
+ix86_set_indirect_branch_type (tree fndecl)
+{
+ if (cfun->machine->indirect_branch_type == indirect_branch_unset)
+ {
+ tree attr = lookup_attribute ("indirect_branch",
+ DECL_ATTRIBUTES (fndecl));
+ if (attr != NULL)
+ {
+ tree args = TREE_VALUE (attr);
+ if (args == NULL)
+ gcc_unreachable ();
+ tree cst = TREE_VALUE (args);
+ if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_keep;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk_inline;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk_extern;
+ else
+ gcc_unreachable ();
+ }
+ else
+ cfun->machine->indirect_branch_type = ix86_indirect_branch;
+ }
+}
+
/* Establish appropriate back-end context for processing the function
FNDECL. The argument might be NULL to indicate processing at top
level, outside of any function scope. */
@@ -5812,7 +5854,10 @@ ix86_set_current_function (tree fndecl)
one is extern inline and one isn't. Call ix86_set_func_type
to set the func_type field. */
if (fndecl != NULL_TREE)
- ix86_set_func_type (fndecl);
+ {
+ ix86_set_func_type (fndecl);
+ ix86_set_indirect_branch_type (fndecl);
+ }
return;
}
@@ -5832,6 +5877,7 @@ ix86_set_current_function (tree fndecl)
}
ix86_set_func_type (fndecl);
+ ix86_set_indirect_branch_type (fndecl);
tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
if (new_tree == NULL_TREE)
@@ -10639,6 +10685,220 @@ ix86_setup_frame_addresses (void)
# endif
#endif
+/* Label count for call and return thunks. It is used to make unique
+ labels in call and return thunks. */
+static int indirectlabelno;
+
+/* True if call and return thunk functions are needed. */
+static bool indirect_thunk_needed = false;
+/* True if call and return thunk functions with the BND prefix are
+ needed. */
+static bool indirect_thunk_bnd_needed = false;
+
+/* Bit masks of integer registers, which contain branch target, used
+ by call and return thunks functions. */
+static int indirect_thunks_used;
+/* Bit masks of integer registers, which contain branch target, used
+ by call and return thunks functions with the BND prefix. */
+static int indirect_thunks_bnd_used;
+
+#ifndef INDIRECT_LABEL
+# define INDIRECT_LABEL "LIND"
+#endif
+
+/* Fills in the label name that should be used for the indirect thunk. */
+
+static void
+indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
+{
+ if (USE_HIDDEN_LINKONCE)
+ {
+ const char *bnd = need_bnd_p ? "_bnd" : "";
+ if (regno >= 0)
+ {
+ const char *reg_prefix;
+ if (LEGACY_INT_REGNO_P (regno))
+ reg_prefix = TARGET_64BIT ? "r" : "e";
+ else
+ reg_prefix = "";
+ sprintf (name, "__x86_indirect_thunk%s_%s%s",
+ bnd, reg_prefix, reg_names[regno]);
+ }
+ else
+ sprintf (name, "__x86_indirect_thunk%s", bnd);
+ }
+ else
+ {
+ if (regno >= 0)
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITBR", regno);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITR", regno);
+ }
+ else
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
+ }
+ }
+}
+
+/* Output a call and return thunk for indirect branch. If BND_P is
+ true, the BND prefix is needed. If REGNO != -1, the function
+ address is in REGNO and the call and return thunk looks like:
+
+ call L2
+ L1:
+ pause
+ jmp L1
+ L2:
+ mov %REG, (%sp)
+ ret
+
+ Otherwise, the function address is on the top of stack and the
+ call and return thunk looks like:
+
+ call L2
+ L1:
+ pause
+ jmp L1
+ L2:
+ lea WORD_SIZE(%sp), %sp
+ ret
+ */
+
+static void
+output_indirect_thunk (bool need_bnd_p, int regno)
+{
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Call */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ /* Pause . */
+ fprintf (asm_out_file, "\tpause\n");
+
+ /* Jump. */
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ if (regno >= 0)
+ {
+ /* MOV. */
+ rtx xops[2];
+ xops[0] = gen_rtx_MEM (word_mode, stack_pointer_rtx);
+ xops[1] = gen_rtx_REG (word_mode, regno);
+ output_asm_insn ("mov\t{%1, %0|%0, %1}", xops);
+ }
+ else
+ {
+ /* LEA. */
+ rtx xops[2];
+ xops[0] = stack_pointer_rtx;
+ xops[1] = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD);
+ output_asm_insn ("lea\t{%E1, %0|%0, %E1}", xops);
+ }
+
+ if (need_bnd_p)
+ fputs ("\tbnd ret\n", asm_out_file);
+ else
+ fputs ("\tret\n", asm_out_file);
+}
+
+/* Output a funtion with a call and return thunk for indirect branch.
+ If BND_P is true, the BND prefix is needed. If REGNO != -1, the
+ function address is in REGNO. Otherwise, the function address is
+ on the top of stack. */
+
+static void
+output_indirect_thunk_function (bool need_bnd_p, int regno)
+{
+ char name[32];
+ tree decl;
+
+ /* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd. */
+ indirect_thunk_name (name, regno, need_bnd_p);
+ decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+ get_identifier (name),
+ build_function_type_list (void_type_node, NULL_TREE));
+ DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
+ NULL_TREE, void_type_node);
+ TREE_PUBLIC (decl) = 1;
+ TREE_STATIC (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+
+#if TARGET_MACHO
+ if (TARGET_MACHO)
+ {
+ switch_to_section (darwin_sections[picbase_thunk_section]);
+ fputs ("\t.weak_definition\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n\t.private_extern\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ putc ('\n', asm_out_file);
+ ASM_OUTPUT_LABEL (asm_out_file, name);
+ DECL_WEAK (decl) = 1;
+ }
+ else
+#endif
+ if (USE_HIDDEN_LINKONCE)
+ {
+ cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl));
+
+ targetm.asm_out.unique_section (decl, 0);
+ switch_to_section (get_named_section (decl, NULL, 0));
+
+ targetm.asm_out.globalize_label (asm_out_file, name);
+ fputs ("\t.hidden\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ putc ('\n', asm_out_file);
+ ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl);
+ }
+ else
+ {
+ switch_to_section (text_section);
+ ASM_OUTPUT_LABEL (asm_out_file, name);
+ }
+
+ DECL_INITIAL (decl) = make_node (BLOCK);
+ current_function_decl = decl;
+ allocate_struct_function (decl, false);
+ init_function_start (decl);
+ /* We're about to hide the function body from callees of final_* by
+ emitting it directly; tell them we're a thunk, if they care. */
+ cfun->is_thunk = true;
+ first_function_block_is_cold = false;
+ /* Make sure unwind info is emitted for the thunk if needed. */
+ final_start_function (emit_barrier (), asm_out_file, 1);
+
+ output_indirect_thunk (need_bnd_p, regno);
+
+ final_end_function ();
+ init_insn_lengths ();
+ free_after_compilation (cfun);
+ set_cfun (NULL);
+ current_function_decl = NULL;
+}
+
static int pic_labels_used;
/* Fills in the label name that should be used for a pc thunk for
@@ -10665,11 +10925,32 @@ ix86_code_end (void)
rtx xops[2];
int regno;
+ if (indirect_thunk_needed)
+ output_indirect_thunk_function (false, -1);
+ if (indirect_thunk_bnd_needed)
+ output_indirect_thunk_function (true, -1);
+
+ for (regno = FIRST_REX_INT_REG; regno <= LAST_REX_INT_REG; regno++)
+ {
+ int i = regno - FIRST_REX_INT_REG + LAST_INT_REG + 1;
+ if ((indirect_thunks_used & (1 << i)))
+ output_indirect_thunk_function (false, regno);
+
+ if ((indirect_thunks_bnd_used & (1 << i)))
+ output_indirect_thunk_function (true, regno);
+ }
+
for (regno = FIRST_INT_REG; regno <= LAST_INT_REG; regno++)
{
char name[32];
tree decl;
+ if ((indirect_thunks_used & (1 << regno)))
+ output_indirect_thunk_function (false, regno);
+
+ if ((indirect_thunks_bnd_used & (1 << regno)))
+ output_indirect_thunk_function (true, regno);
+
if (!(pic_labels_used & (1 << regno)))
continue;
@@ -28150,12 +28431,292 @@ ix86_nopic_noplt_attribute_p (rtx call_op)
return false;
}
+/* Output indirect branch via a call and return thunk. CALL_OP is a
+ register which contains the branch target. XASM is the assembly
+ template for CALL_OP. Branch is a tail call if SIBCALL_P is true.
+ A normal call is converted to:
+
+ call __x86_indirect_thunk_reg
+
+ and a tail call is converted to:
+
+ jmp __x86_indirect_thunk_reg
+ */
+
+static void
+ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
+{
+ char thunk_name_buf[32];
+ char *thunk_name;
+ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ int regno = REGNO (call_op);
+
+ if (cfun->machine->indirect_branch_type
+ != indirect_branch_thunk_inline)
+ {
+ if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+ {
+ int i = regno;
+ if (i >= FIRST_REX_INT_REG)
+ i -= (FIRST_REX_INT_REG - LAST_INT_REG - 1);
+ if (need_bnd_p)
+ indirect_thunks_bnd_used |= 1 << i;
+ else
+ indirect_thunks_used |= 1 << i;
+ }
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ thunk_name = thunk_name_buf;
+ }
+ else
+ thunk_name = NULL;
+
+ if (sibcall_p)
+ {
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+ }
+ else
+ {
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd call\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tcall\t%s\n", thunk_name);
+ return;
+ }
+
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Jump. */
+ if (need_bnd_p)
+ fputs ("\tbnd jmp\t", asm_out_file);
+ else
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ /* Call. */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+ }
+}
+
+/* Output indirect branch via a call and return thunk. CALL_OP is
+ the branch target. XASM is the assembly template for CALL_OP.
+ Branch is a tail call if SIBCALL_P is true. A normal call is
+ converted to:
+
+ jmp L2
+ L1:
+ push CALL_OP
+ jmp __x86_indirect_thunk
+ L2:
+ call L1
+
+ and a tail call is converted to:
+
+ push CALL_OP
+ jmp __x86_indirect_thunk
+ */
+
+static void
+ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
+ bool sibcall_p)
+{
+ char thunk_name_buf[32];
+ char *thunk_name;
+ char push_buf[64];
+ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ int regno = -1;
+
+ if (cfun->machine->indirect_branch_type
+ != indirect_branch_thunk_inline)
+ {
+ if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+ {
+ if (need_bnd_p)
+ indirect_thunk_bnd_needed = true;
+ else
+ indirect_thunk_needed = true;
+ }
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ thunk_name = thunk_name_buf;
+ }
+ else
+ thunk_name = NULL;
+
+ snprintf (push_buf, sizeof (push_buf), "push{%c}\t%s",
+ TARGET_64BIT ? 'q' : 'l', xasm);
+
+ if (sibcall_p)
+ {
+ output_asm_insn (push_buf, &call_op);
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+ }
+ else
+ {
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Jump. */
+ if (need_bnd_p)
+ fputs ("\tbnd jmp\t", asm_out_file);
+ else
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ /* An external function may be called via GOT, instead of PLT. */
+ if (MEM_P (call_op))
+ {
+ struct ix86_address parts;
+ rtx addr = XEXP (call_op, 0);
+ if (ix86_decompose_address (addr, &parts)
+ && parts.base == stack_pointer_rtx)
+ {
+ /* Since call will adjust stack by -UNITS_PER_WORD,
+ we must convert "disp(stack, index, scale)" to
+ "disp+UNITS_PER_WORD(stack, index, scale)". */
+ if (parts.index)
+ {
+ addr = gen_rtx_MULT (Pmode, parts.index,
+ GEN_INT (parts.scale));
+ addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+ addr);
+ }
+ else
+ addr = stack_pointer_rtx;
+
+ rtx disp;
+ if (parts.disp != NULL_RTX)
+ disp = plus_constant (Pmode, parts.disp,
+ UNITS_PER_WORD);
+ else
+ disp = GEN_INT (UNITS_PER_WORD);
+
+ addr = gen_rtx_PLUS (Pmode, addr, disp);
+ call_op = gen_rtx_MEM (GET_MODE (call_op), addr);
+ }
+ }
+
+ output_asm_insn (push_buf, &call_op);
+
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ /* Call. */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+ }
+}
+
+/* Output indirect branch via a call and return thunk. CALL_OP is
+ the branch target. XASM is the assembly template for CALL_OP.
+ Branch is a tail call if SIBCALL_P is true. */
+
+static void
+ix86_output_indirect_branch (rtx call_op, const char *xasm,
+ bool sibcall_p)
+{
+ if (REG_P (call_op))
+ ix86_output_indirect_branch_via_reg (call_op, sibcall_p);
+ else
+ ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p);
+}
+/* Output indirect jump. CALL_OP is the jump target. Jump is a
+ function return if RET_P is true. */
+
+const char *
+ix86_output_indirect_jmp (rtx call_op, bool ret_p)
+{
+ if (cfun->machine->indirect_branch_type != indirect_branch_keep)
+ {
+ /* We can't have red-zone if this isn't a function return since
+ "call" in the indirect thunk pushes the return address onto
+ stack, destroying red-zone. */
+ if (!ret_p && ix86_red_zone_size != 0)
+ gcc_unreachable ();
+
+ ix86_output_indirect_branch (call_op, "%0", true);
+ return "";
+ }
+ else
+ return "%!jmp\t%A0";
+}
+
/* Output the assembly for a call instruction. */
const char *
ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
bool direct_p = constant_call_address_operand (call_op, VOIDmode);
+ bool output_indirect_p
+ = (!TARGET_SEH
+ && cfun->machine->indirect_branch_type != indirect_branch_keep);
bool seh_nop_p = false;
const char *xasm;
@@ -28165,10 +28726,21 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
if (ix86_nopic_noplt_attribute_p (call_op))
{
+ direct_p = false;
if (TARGET_64BIT)
- xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ else
+ xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ }
else
- xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}";
+ else
+ xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ }
}
else
xasm = "%!jmp\t%P0";
@@ -28178,9 +28750,17 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
else if (TARGET_SEH)
xasm = "%!rex.W jmp\t%A0";
else
- xasm = "%!jmp\t%A0";
+ {
+ if (output_indirect_p)
+ xasm = "%0";
+ else
+ xasm = "%!jmp\t%A0";
+ }
- output_asm_insn (xasm, &call_op);
+ if (output_indirect_p && !direct_p)
+ ix86_output_indirect_branch (call_op, xasm, true);
+ else
+ output_asm_insn (xasm, &call_op);
return "";
}
@@ -28218,18 +28798,37 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
if (ix86_nopic_noplt_attribute_p (call_op))
{
+ direct_p = false;
if (TARGET_64BIT)
- xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ else
+ xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ }
else
- xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}";
+ else
+ xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ }
}
else
xasm = "%!call\t%P0";
}
else
- xasm = "%!call\t%A0";
+ {
+ if (output_indirect_p)
+ xasm = "%0";
+ else
+ xasm = "%!call\t%A0";
+ }
- output_asm_insn (xasm, &call_op);
+ if (output_indirect_p && !direct_p)
+ ix86_output_indirect_branch (call_op, xasm, false);
+ else
+ output_asm_insn (xasm, &call_op);
if (seh_nop_p)
return "nop";
@@ -40387,7 +40986,7 @@ ix86_handle_struct_attribute (tree *node, tree name, tree, int,
}
static tree
-ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
+ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
@@ -40396,6 +40995,29 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
name);
*no_add_attrs = true;
}
+
+ if (is_attribute_p ("indirect_branch", name))
+ {
+ tree cst = TREE_VALUE (args);
+ if (TREE_CODE (cst) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qE attribute requires a string constant argument",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
+ {
+ warning (OPT_Wattributes,
+ "argument to %qE attribute is not "
+ "(keep|thunk|thunk-inline|thunk-extern)", name);
+ *no_add_attrs = true;
+ }
+ }
+
return NULL_TREE;
}
@@ -44842,6 +45464,8 @@ static const struct attribute_spec ix86_attribute_table[] =
ix86_handle_no_caller_saved_registers_attribute, NULL },
{ "naked", 0, 0, true, false, false, false,
ix86_handle_fndecl_attribute, NULL },
+ { "indirect_branch", 1, 1, true, false, false, false,
+ ix86_handle_fndecl_attribute, NULL },
/* End element. */
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 933f261ea66..3b939086112 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2570,6 +2570,13 @@ struct GTY(()) machine_function {
/* Function type. */
ENUM_BITFIELD(function_type) func_type : 2;
+ /* How to generate indirec branch. */
+ ENUM_BITFIELD(indirect_branch) indirect_branch_type : 3;
+
+ /* If true, the current function has local indirect jumps, like
+ "indirect_jump" or "tablejump". */
+ BOOL_BITFIELD has_local_indirect_jump : 1;
+
/* If true, the current function is a function specified with
the "interrupt" or "no_caller_saved_registers" attribute. */
BOOL_BITFIELD no_caller_saved_registers : 1;
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 3f587806407..1ba144da87e 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -12313,13 +12313,18 @@
{
if (TARGET_X32)
operands[0] = convert_memory_address (word_mode, operands[0]);
+ cfun->machine->has_local_indirect_jump = true;
})
(define_insn "*indirect_jump"
[(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))]
""
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], false);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
@@ -12362,14 +12367,19 @@
if (TARGET_X32)
operands[0] = convert_memory_address (word_mode, operands[0]);
+ cfun->machine->has_local_indirect_jump = true;
})
(define_insn "*tablejump_1"
[(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))
(use (label_ref (match_operand 1)))]
""
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], false);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
\f
@@ -13097,8 +13107,12 @@
[(simple_return)
(use (match_operand 0 "register_operand" "r"))]
"reload_completed"
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], true);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 09aaa97c2fc..59e5cc8e7e4 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -1021,3 +1021,23 @@ indirect jump.
mforce-indirect-call
Target Report Var(flag_force_indirect_call) Init(0)
Make all function calls indirect.
+
+mindirect-branch=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep)
+Convert indirect call and jump to call and return thunks.
+
+Enum
+Name(indirect_branch) Type(enum indirect_branch)
+Known indirect branch choices (for use with the -mindirect-branch= option):
+
+EnumValue
+Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
+
+EnumValue
+Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk)
+
+EnumValue
+Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
+
+EnumValue
+Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f3e4a63ab46..ddb6035be96 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5754,6 +5754,16 @@ Specify which floating-point unit to use. You must specify the
@code{target("fpmath=sse+387")} because the comma would separate
different options.
+@item indirect_branch("@var{choice}")
+@cindex @code{indirect_branch} function attribute, x86
+On x86 targets, the @code{indirect_branch} attribute causes the compiler
+to convert indirect call and jump with @var{choice}. @samp{keep}
+keeps indirect call and jump unmodified. @samp{thunk} converts indirect
+call and jump to call and return thunk. @samp{thunk-inline} converts
+indirect call and jump to inlined call and return thunk.
+@samp{thunk-extern} converts indirect call and jump to external call
+and return thunk provided in a separate object file.
+
@item nocf_check
@cindex @code{nocf_check} function attribute
The @code{nocf_check} attribute on a function is used to inform the
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a9449a86064..0d685c3576b 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1229,7 +1229,8 @@ See RS/6000 and PowerPC Options.
-mstack-protector-guard-reg=@var{reg} @gol
-mstack-protector-guard-offset=@var{offset} @gol
-mstack-protector-guard-symbol=@var{symbol} -mmitigate-rop @gol
--mgeneral-regs-only -mcall-ms2sysv-xlogues}
+-mgeneral-regs-only -mcall-ms2sysv-xlogues @gol
+-mindirect-branch=@var{choice}}
@emph{x86 Windows Options}
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
@@ -26838,6 +26839,17 @@ Generate code that uses only the general-purpose registers. This
prevents the compiler from using floating-point, vector, mask and bound
registers.
+@item -mindirect-branch=@var{choice}
+@opindex -mindirect-branch
+Convert indirect call and jump with @var{choice}. The default is
+@samp{keep}, which keeps indirect call and jump unmodified.
+@samp{thunk} converts indirect call and jump to call and return thunk.
+@samp{thunk-inline} converts indirect call and jump to inlined call
+and return thunk. @samp{thunk-extern} converts indirect call and jump
+to external call and return thunk provided in a separate object file.
+You can control this behavior for a specific function by using the
+function attribute @code{indirect_branch}. @xref{Function Attributes}.
+
@end table
These @samp{-m} switches are supported in addition to the above
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
new file mode 100644
index 00000000000..d1d2ee78797
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
new file mode 100644
index 00000000000..08646c6b823
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
new file mode 100644
index 00000000000..af244de2238
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
new file mode 100644
index 00000000000..b8aedd5a4e6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
new file mode 100644
index 00000000000..6ffb9235f94
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
new file mode 100644
index 00000000000..e6d9d148cd2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
new file mode 100644
index 00000000000..d892d8f5992
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
new file mode 100644
index 00000000000..24188d0b62d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+extern void male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk")));
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
new file mode 100644
index 00000000000..03184b90cda
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk")))
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
new file mode 100644
index 00000000000..af167840b81
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+extern int male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk-inline")));
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
new file mode 100644
index 00000000000..146124894a0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk-inline")))
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
new file mode 100644
index 00000000000..568327cd8e7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+extern int male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk-extern")));
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
new file mode 100644
index 00000000000..bd8a99e7828
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk-extern")))
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
new file mode 100644
index 00000000000..356015c9799
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
new file mode 100644
index 00000000000..6960fa0bbfb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+__attribute__ ((indirect_branch("keep")))
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
new file mode 100644
index 00000000000..febf32d76ea
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
@@ -0,0 +1,19 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+
+void (*dispatch) (char *);
+char buf[10];
+
+void
+foo (void)
+{
+ dispatch (buf);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
new file mode 100644
index 00000000000..319ba30b78b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+
+void (*dispatch) (char *);
+char buf[10];
+
+int
+foo (void)
+{
+ dispatch (buf);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
new file mode 100644
index 00000000000..9168b3146f5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+
+void bar (char *);
+char buf[10];
+
+void
+foo (void)
+{
+ bar (buf);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
new file mode 100644
index 00000000000..d3b36d44c7c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
@@ -0,0 +1,19 @@
+/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+
+void bar (char *);
+char buf[10];
+
+int
+foo (void)
+{
+ bar (buf);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-times "bnd call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
new file mode 100644
index 00000000000..9e50b282f77
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
new file mode 100644
index 00000000000..f897d1c0497
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
new file mode 100644
index 00000000000..25905cd0016
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
new file mode 100644
index 00000000000..a7fa12183af
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
new file mode 100644
index 00000000000..48a49760be6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
new file mode 100644
index 00000000000..a1c662f7d23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
new file mode 100644
index 00000000000..40a665ea640
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
new file mode 100644
index 00000000000..3ace8d1b031
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
new file mode 100644
index 00000000000..6c97b96f1f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
new file mode 100644
index 00000000000..8f6759cbf06
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
new file mode 100644
index 00000000000..b07d08cab0f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
new file mode 100644
index 00000000000..10794886b1b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
new file mode 100644
index 00000000000..a26ec4b06ed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \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-not "__x86_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
new file mode 100644
index 00000000000..77253af17c6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/5] x86: Add -mindirect-branch=
2018-01-14 3:37 ` [PATCH 1/5] x86: Add -mindirect-branch= H.J. Lu
@ 2018-01-14 10:46 ` Jan Hubicka
2018-01-14 11:06 ` Markus Trippelsdorf
0 siblings, 1 reply; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 10:46 UTC (permalink / raw)
To: H.J. Lu; +Cc: gcc-patches
> gcc/
>
> * config/i386/i386-opts.h (indirect_branch): New.
> * config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
> * config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
> with local indirect jump when converting indirect call and jump.
> (ix86_set_indirect_branch_type): New.
> (ix86_set_current_function): Call ix86_set_indirect_branch_type.
> (indirectlabelno): New.
> (indirect_thunk_needed): Likewise.
> (indirect_thunk_bnd_needed): Likewise.
> (indirect_thunks_used): Likewise.
> (indirect_thunks_bnd_used): Likewise.
> (INDIRECT_LABEL): Likewise.
> (indirect_thunk_name): Likewise.
> (output_indirect_thunk): Likewise.
> (output_indirect_thunk_function): Likewise.
> (ix86_output_indirect_branch): Likewise.
> (ix86_output_indirect_jmp): Likewise.
> (ix86_code_end): Call output_indirect_thunk_function if needed.
> (ix86_output_call_insn): Call ix86_output_indirect_branch if
> needed.
> (ix86_handle_fndecl_attribute): Handle indirect_branch.
> (ix86_attribute_table): Add indirect_branch.
> * config/i386/i386.h (machine_function): Add indirect_branch_type
> and has_local_indirect_jump.
> * config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
> to true.
> (tablejump): Likewise.
> (*indirect_jump): Use ix86_output_indirect_jmp.
> (*tablejump_1): Likewise.
> (simple_return_indirect_internal): Likewise.
> * config/i386/i386.opt (mindirect-branch=): New option.
> (indirect_branch): New.
> (keep): Likewise.
> (thunk): Likewise.
> (thunk-inline): Likewise.
> (thunk-extern): Likewise.
> * doc/extend.texi: Document indirect_branch function attribute.
> * doc/invoke.texi: Document -mindirect-branch= option.
>
> gcc/testsuite/
>
> * gcc.target/i386/indirect-thunk-1.c: New test.
> * gcc.target/i386/indirect-thunk-2.c: Likewise.
> * gcc.target/i386/indirect-thunk-3.c: Likewise.
> * gcc.target/i386/indirect-thunk-4.c: Likewise.
> * gcc.target/i386/indirect-thunk-5.c: Likewise.
> * gcc.target/i386/indirect-thunk-6.c: Likewise.
> * gcc.target/i386/indirect-thunk-7.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
> * gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
> * gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
> * gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
> * gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
> * gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
> * gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
> * gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
> * gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
> * gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
> * gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
> * gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
> * gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
> * gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
> * gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
> * gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
> * gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
> * gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
> * gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
> * gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
> +
> + /* Pause . */
> + fprintf (asm_out_file, "\tpause\n");
OK, but please prepare incremental patches to choose between pause and lefence
as needed for AMD CPUs and check for large code model.
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/5] x86: Add -mindirect-branch=
2018-01-14 10:46 ` Jan Hubicka
@ 2018-01-14 11:06 ` Markus Trippelsdorf
2018-01-14 11:21 ` Jan Hubicka
0 siblings, 1 reply; 120+ messages in thread
From: Markus Trippelsdorf @ 2018-01-14 11:06 UTC (permalink / raw)
To: Jan Hubicka; +Cc: H.J. Lu, gcc-patches
On 2018.01.14 at 11:46 +0100, Jan Hubicka wrote:
> > gcc/
> >
> > * config/i386/i386-opts.h (indirect_branch): New.
> > * config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
> > * config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
> > with local indirect jump when converting indirect call and jump.
> > (ix86_set_indirect_branch_type): New.
> > (ix86_set_current_function): Call ix86_set_indirect_branch_type.
> > (indirectlabelno): New.
> > (indirect_thunk_needed): Likewise.
> > (indirect_thunk_bnd_needed): Likewise.
> > (indirect_thunks_used): Likewise.
> > (indirect_thunks_bnd_used): Likewise.
> > (INDIRECT_LABEL): Likewise.
> > (indirect_thunk_name): Likewise.
> > (output_indirect_thunk): Likewise.
> > (output_indirect_thunk_function): Likewise.
> > (ix86_output_indirect_branch): Likewise.
> > (ix86_output_indirect_jmp): Likewise.
> > (ix86_code_end): Call output_indirect_thunk_function if needed.
> > (ix86_output_call_insn): Call ix86_output_indirect_branch if
> > needed.
> > (ix86_handle_fndecl_attribute): Handle indirect_branch.
> > (ix86_attribute_table): Add indirect_branch.
> > * config/i386/i386.h (machine_function): Add indirect_branch_type
> > and has_local_indirect_jump.
> > * config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
> > to true.
> > (tablejump): Likewise.
> > (*indirect_jump): Use ix86_output_indirect_jmp.
> > (*tablejump_1): Likewise.
> > (simple_return_indirect_internal): Likewise.
> > * config/i386/i386.opt (mindirect-branch=): New option.
> > (indirect_branch): New.
> > (keep): Likewise.
> > (thunk): Likewise.
> > (thunk-inline): Likewise.
> > (thunk-extern): Likewise.
> > * doc/extend.texi: Document indirect_branch function attribute.
> > * doc/invoke.texi: Document -mindirect-branch= option.
> > +
> > + /* Pause . */
> > + fprintf (asm_out_file, "\tpause\n");
>
> OK, but please prepare incremental patches to choose between pause and lefence
> as needed for AMD CPUs and check for large code model.
Why not use both? That would make everybody happy, no?
pause
lfence
jmp
--
Markus
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/5] x86: Add -mindirect-branch=
2018-01-14 11:06 ` Markus Trippelsdorf
@ 2018-01-14 11:21 ` Jan Hubicka
2018-01-14 12:53 ` H.J. Lu
2018-01-14 20:21 ` David Woodhouse
0 siblings, 2 replies; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 11:21 UTC (permalink / raw)
To: Markus Trippelsdorf; +Cc: H.J. Lu, gcc-patches
> On 2018.01.14 at 11:46 +0100, Jan Hubicka wrote:
> > > gcc/
> > >
> > > * config/i386/i386-opts.h (indirect_branch): New.
> > > * config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
> > > * config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
> > > with local indirect jump when converting indirect call and jump.
> > > (ix86_set_indirect_branch_type): New.
> > > (ix86_set_current_function): Call ix86_set_indirect_branch_type.
> > > (indirectlabelno): New.
> > > (indirect_thunk_needed): Likewise.
> > > (indirect_thunk_bnd_needed): Likewise.
> > > (indirect_thunks_used): Likewise.
> > > (indirect_thunks_bnd_used): Likewise.
> > > (INDIRECT_LABEL): Likewise.
> > > (indirect_thunk_name): Likewise.
> > > (output_indirect_thunk): Likewise.
> > > (output_indirect_thunk_function): Likewise.
> > > (ix86_output_indirect_branch): Likewise.
> > > (ix86_output_indirect_jmp): Likewise.
> > > (ix86_code_end): Call output_indirect_thunk_function if needed.
> > > (ix86_output_call_insn): Call ix86_output_indirect_branch if
> > > needed.
> > > (ix86_handle_fndecl_attribute): Handle indirect_branch.
> > > (ix86_attribute_table): Add indirect_branch.
> > > * config/i386/i386.h (machine_function): Add indirect_branch_type
> > > and has_local_indirect_jump.
> > > * config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
> > > to true.
> > > (tablejump): Likewise.
> > > (*indirect_jump): Use ix86_output_indirect_jmp.
> > > (*tablejump_1): Likewise.
> > > (simple_return_indirect_internal): Likewise.
> > > * config/i386/i386.opt (mindirect-branch=): New option.
> > > (indirect_branch): New.
> > > (keep): Likewise.
> > > (thunk): Likewise.
> > > (thunk-inline): Likewise.
> > > (thunk-extern): Likewise.
> > > * doc/extend.texi: Document indirect_branch function attribute.
> > > * doc/invoke.texi: Document -mindirect-branch= option.
> > > +
> > > + /* Pause . */
> > > + fprintf (asm_out_file, "\tpause\n");
> >
> > OK, but please prepare incremental patches to choose between pause and lefence
> > as needed for AMD CPUs and check for large code model.
>
> Why not use both? That would make everybody happy, no?
>
> pause
> lfence
> jmp
Yes, according to my understanding we want both pause and lefence by default
(it is expensive anyway) + command line option to control which one to use?
Probably thus the first patch should default to both.
Honza
>
> --
> Markus
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/5] x86: Add -mindirect-branch=
2018-01-14 11:21 ` Jan Hubicka
@ 2018-01-14 12:53 ` H.J. Lu
2018-01-14 15:11 ` H.J. Lu
2018-01-14 20:21 ` David Woodhouse
1 sibling, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 12:53 UTC (permalink / raw)
To: Jan Hubicka; +Cc: Markus Trippelsdorf, GCC Patches
[-- Attachment #1: Type: text/plain, Size: 2646 bytes --]
On Sun, Jan 14, 2018 at 3:06 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> On 2018.01.14 at 11:46 +0100, Jan Hubicka wrote:
>> > > gcc/
>> > >
>> > > * config/i386/i386-opts.h (indirect_branch): New.
>> > > * config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
>> > > * config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
>> > > with local indirect jump when converting indirect call and jump.
>> > > (ix86_set_indirect_branch_type): New.
>> > > (ix86_set_current_function): Call ix86_set_indirect_branch_type.
>> > > (indirectlabelno): New.
>> > > (indirect_thunk_needed): Likewise.
>> > > (indirect_thunk_bnd_needed): Likewise.
>> > > (indirect_thunks_used): Likewise.
>> > > (indirect_thunks_bnd_used): Likewise.
>> > > (INDIRECT_LABEL): Likewise.
>> > > (indirect_thunk_name): Likewise.
>> > > (output_indirect_thunk): Likewise.
>> > > (output_indirect_thunk_function): Likewise.
>> > > (ix86_output_indirect_branch): Likewise.
>> > > (ix86_output_indirect_jmp): Likewise.
>> > > (ix86_code_end): Call output_indirect_thunk_function if needed.
>> > > (ix86_output_call_insn): Call ix86_output_indirect_branch if
>> > > needed.
>> > > (ix86_handle_fndecl_attribute): Handle indirect_branch.
>> > > (ix86_attribute_table): Add indirect_branch.
>> > > * config/i386/i386.h (machine_function): Add indirect_branch_type
>> > > and has_local_indirect_jump.
>> > > * config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
>> > > to true.
>> > > (tablejump): Likewise.
>> > > (*indirect_jump): Use ix86_output_indirect_jmp.
>> > > (*tablejump_1): Likewise.
>> > > (simple_return_indirect_internal): Likewise.
>> > > * config/i386/i386.opt (mindirect-branch=): New option.
>> > > (indirect_branch): New.
>> > > (keep): Likewise.
>> > > (thunk): Likewise.
>> > > (thunk-inline): Likewise.
>> > > (thunk-extern): Likewise.
>> > > * doc/extend.texi: Document indirect_branch function attribute.
>> > > * doc/invoke.texi: Document -mindirect-branch= option.
>> > > +
>> > > + /* Pause . */
>> > > + fprintf (asm_out_file, "\tpause\n");
>> >
>> > OK, but please prepare incremental patches to choose between pause and lefence
>> > as needed for AMD CPUs and check for large code model.
>>
>> Why not use both? That would make everybody happy, no?
>>
>> pause
>> lfence
>> jmp
>
> Yes, according to my understanding we want both pause and lefence by default
> (it is expensive anyway) + command line option to control which one to use?
>
> Probably thus the first patch should default to both.
Done. This is the patch I am checking in.
--
H.J.
[-- Attachment #2: 0001-x86-Add-mindirect-branch.patch --]
[-- Type: text/x-patch, Size: 72825 bytes --]
From de23211f9a2fae02fe003f7b32016b94be9c8407 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 6 Jan 2018 22:29:55 -0800
Subject: [PATCH 1/6] x86: Add -mindirect-branch=
Add -mindirect-branch= option to convert indirect call and jump to call
and return thunks. The default is 'keep', which keeps indirect call and
jump unmodified. 'thunk' converts indirect call and jump to call and
return thunk. 'thunk-inline' converts indirect call and jump to inlined
call and return thunk. 'thunk-extern' converts indirect call and jump to
external call and return thunk provided in a separate object file. You
can control this behavior for a specific function by using the function
attribute indirect_branch.
2 kinds of thunks are geneated. Memory thunk where the function address
is at the top of the stack:
__x86_indirect_thunk:
call L2
L1:
pause
lfence
jmp L1
L2:
lea 8(%rsp), %rsp|lea 4(%esp), %esp
ret
Indirect jmp via memory, "jmp mem", is converted to
push memory
jmp __x86_indirect_thunk
Indirect call via memory, "call mem", is converted to
jmp L2
L1:
push [mem]
jmp __x86_indirect_thunk
L2:
call L1
Register thunk where the function address is in a register, reg:
__x86_indirect_thunk_reg:
call L2
L1:
pause
lfence
jmp L1
L2:
movq %reg, (%rsp)|movl %reg, (%esp)
ret
where reg is one of (r|e)ax, (r|e)dx, (r|e)cx, (r|e)bx, (r|e)si, (r|e)di,
(r|e)bp, r8, r9, r10, r11, r12, r13, r14 and r15.
Indirect jmp via register, "jmp reg", is converted to
jmp __x86_indirect_thunk_reg
Indirect call via register, "call reg", is converted to
call __x86_indirect_thunk_reg
gcc/
* config/i386/i386-opts.h (indirect_branch): New.
* config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
* config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
with local indirect jump when converting indirect call and jump.
(ix86_set_indirect_branch_type): New.
(ix86_set_current_function): Call ix86_set_indirect_branch_type.
(indirectlabelno): New.
(indirect_thunk_needed): Likewise.
(indirect_thunk_bnd_needed): Likewise.
(indirect_thunks_used): Likewise.
(indirect_thunks_bnd_used): Likewise.
(INDIRECT_LABEL): Likewise.
(indirect_thunk_name): Likewise.
(output_indirect_thunk): Likewise.
(output_indirect_thunk_function): Likewise.
(ix86_output_indirect_branch): Likewise.
(ix86_output_indirect_jmp): Likewise.
(ix86_code_end): Call output_indirect_thunk_function if needed.
(ix86_output_call_insn): Call ix86_output_indirect_branch if
needed.
(ix86_handle_fndecl_attribute): Handle indirect_branch.
(ix86_attribute_table): Add indirect_branch.
* config/i386/i386.h (machine_function): Add indirect_branch_type
and has_local_indirect_jump.
* config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
to true.
(tablejump): Likewise.
(*indirect_jump): Use ix86_output_indirect_jmp.
(*tablejump_1): Likewise.
(simple_return_indirect_internal): Likewise.
* config/i386/i386.opt (mindirect-branch=): New option.
(indirect_branch): New.
(keep): Likewise.
(thunk): Likewise.
(thunk-inline): Likewise.
(thunk-extern): Likewise.
* doc/extend.texi: Document indirect_branch function attribute.
* doc/invoke.texi: Document -mindirect-branch= option.
gcc/testsuite/
* gcc.target/i386/indirect-thunk-1.c: New test.
* gcc.target/i386/indirect-thunk-2.c: Likewise.
* gcc.target/i386/indirect-thunk-3.c: Likewise.
* gcc.target/i386/indirect-thunk-4.c: Likewise.
* gcc.target/i386/indirect-thunk-5.c: Likewise.
* gcc.target/i386/indirect-thunk-6.c: Likewise.
* gcc.target/i386/indirect-thunk-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
---
gcc/config/i386/i386-opts.h | 13 +
gcc/config/i386/i386-protos.h | 1 +
gcc/config/i386/i386.c | 648 ++++++++++++++++++++-
gcc/config/i386/i386.h | 7 +
gcc/config/i386/i386.md | 26 +-
gcc/config/i386/i386.opt | 20 +
gcc/doc/extend.texi | 10 +
gcc/doc/invoke.texi | 14 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 20 +
gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 20 +
gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 21 +
gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 21 +
gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 17 +
gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 18 +
gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 44 ++
.../gcc.target/i386/indirect-thunk-attr-1.c | 23 +
.../gcc.target/i386/indirect-thunk-attr-2.c | 21 +
.../gcc.target/i386/indirect-thunk-attr-3.c | 23 +
.../gcc.target/i386/indirect-thunk-attr-4.c | 22 +
.../gcc.target/i386/indirect-thunk-attr-5.c | 22 +
.../gcc.target/i386/indirect-thunk-attr-6.c | 21 +
.../gcc.target/i386/indirect-thunk-attr-7.c | 44 ++
.../gcc.target/i386/indirect-thunk-attr-8.c | 42 ++
.../gcc.target/i386/indirect-thunk-bnd-1.c | 20 +
.../gcc.target/i386/indirect-thunk-bnd-2.c | 21 +
.../gcc.target/i386/indirect-thunk-bnd-3.c | 19 +
.../gcc.target/i386/indirect-thunk-bnd-4.c | 20 +
.../gcc.target/i386/indirect-thunk-extern-1.c | 19 +
.../gcc.target/i386/indirect-thunk-extern-2.c | 19 +
.../gcc.target/i386/indirect-thunk-extern-3.c | 20 +
.../gcc.target/i386/indirect-thunk-extern-4.c | 20 +
.../gcc.target/i386/indirect-thunk-extern-5.c | 16 +
.../gcc.target/i386/indirect-thunk-extern-6.c | 17 +
.../gcc.target/i386/indirect-thunk-extern-7.c | 43 ++
.../gcc.target/i386/indirect-thunk-inline-1.c | 20 +
.../gcc.target/i386/indirect-thunk-inline-2.c | 20 +
.../gcc.target/i386/indirect-thunk-inline-3.c | 21 +
.../gcc.target/i386/indirect-thunk-inline-4.c | 21 +
.../gcc.target/i386/indirect-thunk-inline-5.c | 17 +
.../gcc.target/i386/indirect-thunk-inline-6.c | 18 +
.../gcc.target/i386/indirect-thunk-inline-7.c | 44 ++
41 files changed, 1494 insertions(+), 19 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h
index f245c1573cf..46366cbfa72 100644
--- a/gcc/config/i386/i386-opts.h
+++ b/gcc/config/i386/i386-opts.h
@@ -106,4 +106,17 @@ enum prefer_vector_width {
PVW_AVX512
};
+/* This is used to mitigate variant #2 of the speculative execution
+ vulnerabilities on x86 processors identified by CVE-2017-5715, aka
+ Spectre. They convert indirect branches and function returns to
+ call and return thunks to avoid speculative execution via indirect
+ call, jmp and ret. */
+enum indirect_branch {
+ indirect_branch_unset = 0,
+ indirect_branch_keep,
+ indirect_branch_thunk,
+ indirect_branch_thunk_inline,
+ indirect_branch_thunk_extern
+};
+
#endif
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 0e49652898c..bf11cc426f9 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -305,6 +305,7 @@ extern enum attr_cpu ix86_schedule;
#endif
extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
+extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
machine_mode mode);
extern int ix86_min_insn_size (rtx_insn *);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 5ee3be386df..43524f28d29 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2724,12 +2724,23 @@ make_pass_insert_endbranch (gcc::context *ctxt)
return new pass_insert_endbranch (ctxt);
}
-/* Return true if a red-zone is in use. */
+/* Return true if a red-zone is in use. We can't use red-zone when
+ there are local indirect jumps, like "indirect_jump" or "tablejump",
+ which jumps to another place in the function, since "call" in the
+ indirect thunk pushes the return address onto stack, destroying
+ red-zone.
+
+ TODO: If we can reserve the first 2 WORDs, for PUSH and, another
+ for CALL, in red-zone, we can allow local indirect jumps with
+ indirect thunk. */
bool
ix86_using_red_zone (void)
{
- return TARGET_RED_ZONE && !TARGET_64BIT_MS_ABI;
+ return (TARGET_RED_ZONE
+ && !TARGET_64BIT_MS_ABI
+ && (!cfun->machine->has_local_indirect_jump
+ || cfun->machine->indirect_branch_type == indirect_branch_keep));
}
\f
/* Return a string that documents the current -m options. The caller is
@@ -5797,6 +5808,37 @@ ix86_set_func_type (tree fndecl)
}
}
+/* Set the indirect_branch_type field from the function FNDECL. */
+
+static void
+ix86_set_indirect_branch_type (tree fndecl)
+{
+ if (cfun->machine->indirect_branch_type == indirect_branch_unset)
+ {
+ tree attr = lookup_attribute ("indirect_branch",
+ DECL_ATTRIBUTES (fndecl));
+ if (attr != NULL)
+ {
+ tree args = TREE_VALUE (attr);
+ if (args == NULL)
+ gcc_unreachable ();
+ tree cst = TREE_VALUE (args);
+ if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_keep;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk_inline;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk_extern;
+ else
+ gcc_unreachable ();
+ }
+ else
+ cfun->machine->indirect_branch_type = ix86_indirect_branch;
+ }
+}
+
/* Establish appropriate back-end context for processing the function
FNDECL. The argument might be NULL to indicate processing at top
level, outside of any function scope. */
@@ -5812,7 +5854,10 @@ ix86_set_current_function (tree fndecl)
one is extern inline and one isn't. Call ix86_set_func_type
to set the func_type field. */
if (fndecl != NULL_TREE)
- ix86_set_func_type (fndecl);
+ {
+ ix86_set_func_type (fndecl);
+ ix86_set_indirect_branch_type (fndecl);
+ }
return;
}
@@ -5832,6 +5877,7 @@ ix86_set_current_function (tree fndecl)
}
ix86_set_func_type (fndecl);
+ ix86_set_indirect_branch_type (fndecl);
tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
if (new_tree == NULL_TREE)
@@ -10639,6 +10685,220 @@ ix86_setup_frame_addresses (void)
# endif
#endif
+/* Label count for call and return thunks. It is used to make unique
+ labels in call and return thunks. */
+static int indirectlabelno;
+
+/* True if call and return thunk functions are needed. */
+static bool indirect_thunk_needed = false;
+/* True if call and return thunk functions with the BND prefix are
+ needed. */
+static bool indirect_thunk_bnd_needed = false;
+
+/* Bit masks of integer registers, which contain branch target, used
+ by call and return thunks functions. */
+static int indirect_thunks_used;
+/* Bit masks of integer registers, which contain branch target, used
+ by call and return thunks functions with the BND prefix. */
+static int indirect_thunks_bnd_used;
+
+#ifndef INDIRECT_LABEL
+# define INDIRECT_LABEL "LIND"
+#endif
+
+/* Fills in the label name that should be used for the indirect thunk. */
+
+static void
+indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
+{
+ if (USE_HIDDEN_LINKONCE)
+ {
+ const char *bnd = need_bnd_p ? "_bnd" : "";
+ if (regno >= 0)
+ {
+ const char *reg_prefix;
+ if (LEGACY_INT_REGNO_P (regno))
+ reg_prefix = TARGET_64BIT ? "r" : "e";
+ else
+ reg_prefix = "";
+ sprintf (name, "__x86_indirect_thunk%s_%s%s",
+ bnd, reg_prefix, reg_names[regno]);
+ }
+ else
+ sprintf (name, "__x86_indirect_thunk%s", bnd);
+ }
+ else
+ {
+ if (regno >= 0)
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITBR", regno);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITR", regno);
+ }
+ else
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
+ }
+ }
+}
+
+/* Output a call and return thunk for indirect branch. If BND_P is
+ true, the BND prefix is needed. If REGNO != -1, the function
+ address is in REGNO and the call and return thunk looks like:
+
+ call L2
+ L1:
+ pause
+ jmp L1
+ L2:
+ mov %REG, (%sp)
+ ret
+
+ Otherwise, the function address is on the top of stack and the
+ call and return thunk looks like:
+
+ call L2
+ L1:
+ pause
+ jmp L1
+ L2:
+ lea WORD_SIZE(%sp), %sp
+ ret
+ */
+
+static void
+output_indirect_thunk (bool need_bnd_p, int regno)
+{
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Call */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ /* Pause + lfence. */
+ fprintf (asm_out_file, "\tpause\n\tlfence\n");
+
+ /* Jump. */
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ if (regno >= 0)
+ {
+ /* MOV. */
+ rtx xops[2];
+ xops[0] = gen_rtx_MEM (word_mode, stack_pointer_rtx);
+ xops[1] = gen_rtx_REG (word_mode, regno);
+ output_asm_insn ("mov\t{%1, %0|%0, %1}", xops);
+ }
+ else
+ {
+ /* LEA. */
+ rtx xops[2];
+ xops[0] = stack_pointer_rtx;
+ xops[1] = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD);
+ output_asm_insn ("lea\t{%E1, %0|%0, %E1}", xops);
+ }
+
+ if (need_bnd_p)
+ fputs ("\tbnd ret\n", asm_out_file);
+ else
+ fputs ("\tret\n", asm_out_file);
+}
+
+/* Output a funtion with a call and return thunk for indirect branch.
+ If BND_P is true, the BND prefix is needed. If REGNO != -1, the
+ function address is in REGNO. Otherwise, the function address is
+ on the top of stack. */
+
+static void
+output_indirect_thunk_function (bool need_bnd_p, int regno)
+{
+ char name[32];
+ tree decl;
+
+ /* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd. */
+ indirect_thunk_name (name, regno, need_bnd_p);
+ decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+ get_identifier (name),
+ build_function_type_list (void_type_node, NULL_TREE));
+ DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
+ NULL_TREE, void_type_node);
+ TREE_PUBLIC (decl) = 1;
+ TREE_STATIC (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+
+#if TARGET_MACHO
+ if (TARGET_MACHO)
+ {
+ switch_to_section (darwin_sections[picbase_thunk_section]);
+ fputs ("\t.weak_definition\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n\t.private_extern\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ putc ('\n', asm_out_file);
+ ASM_OUTPUT_LABEL (asm_out_file, name);
+ DECL_WEAK (decl) = 1;
+ }
+ else
+#endif
+ if (USE_HIDDEN_LINKONCE)
+ {
+ cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl));
+
+ targetm.asm_out.unique_section (decl, 0);
+ switch_to_section (get_named_section (decl, NULL, 0));
+
+ targetm.asm_out.globalize_label (asm_out_file, name);
+ fputs ("\t.hidden\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ putc ('\n', asm_out_file);
+ ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl);
+ }
+ else
+ {
+ switch_to_section (text_section);
+ ASM_OUTPUT_LABEL (asm_out_file, name);
+ }
+
+ DECL_INITIAL (decl) = make_node (BLOCK);
+ current_function_decl = decl;
+ allocate_struct_function (decl, false);
+ init_function_start (decl);
+ /* We're about to hide the function body from callees of final_* by
+ emitting it directly; tell them we're a thunk, if they care. */
+ cfun->is_thunk = true;
+ first_function_block_is_cold = false;
+ /* Make sure unwind info is emitted for the thunk if needed. */
+ final_start_function (emit_barrier (), asm_out_file, 1);
+
+ output_indirect_thunk (need_bnd_p, regno);
+
+ final_end_function ();
+ init_insn_lengths ();
+ free_after_compilation (cfun);
+ set_cfun (NULL);
+ current_function_decl = NULL;
+}
+
static int pic_labels_used;
/* Fills in the label name that should be used for a pc thunk for
@@ -10665,11 +10925,32 @@ ix86_code_end (void)
rtx xops[2];
int regno;
+ if (indirect_thunk_needed)
+ output_indirect_thunk_function (false, -1);
+ if (indirect_thunk_bnd_needed)
+ output_indirect_thunk_function (true, -1);
+
+ for (regno = FIRST_REX_INT_REG; regno <= LAST_REX_INT_REG; regno++)
+ {
+ int i = regno - FIRST_REX_INT_REG + LAST_INT_REG + 1;
+ if ((indirect_thunks_used & (1 << i)))
+ output_indirect_thunk_function (false, regno);
+
+ if ((indirect_thunks_bnd_used & (1 << i)))
+ output_indirect_thunk_function (true, regno);
+ }
+
for (regno = FIRST_INT_REG; regno <= LAST_INT_REG; regno++)
{
char name[32];
tree decl;
+ if ((indirect_thunks_used & (1 << regno)))
+ output_indirect_thunk_function (false, regno);
+
+ if ((indirect_thunks_bnd_used & (1 << regno)))
+ output_indirect_thunk_function (true, regno);
+
if (!(pic_labels_used & (1 << regno)))
continue;
@@ -28150,12 +28431,292 @@ ix86_nopic_noplt_attribute_p (rtx call_op)
return false;
}
+/* Output indirect branch via a call and return thunk. CALL_OP is a
+ register which contains the branch target. XASM is the assembly
+ template for CALL_OP. Branch is a tail call if SIBCALL_P is true.
+ A normal call is converted to:
+
+ call __x86_indirect_thunk_reg
+
+ and a tail call is converted to:
+
+ jmp __x86_indirect_thunk_reg
+ */
+
+static void
+ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
+{
+ char thunk_name_buf[32];
+ char *thunk_name;
+ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ int regno = REGNO (call_op);
+
+ if (cfun->machine->indirect_branch_type
+ != indirect_branch_thunk_inline)
+ {
+ if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+ {
+ int i = regno;
+ if (i >= FIRST_REX_INT_REG)
+ i -= (FIRST_REX_INT_REG - LAST_INT_REG - 1);
+ if (need_bnd_p)
+ indirect_thunks_bnd_used |= 1 << i;
+ else
+ indirect_thunks_used |= 1 << i;
+ }
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ thunk_name = thunk_name_buf;
+ }
+ else
+ thunk_name = NULL;
+
+ if (sibcall_p)
+ {
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+ }
+ else
+ {
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd call\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tcall\t%s\n", thunk_name);
+ return;
+ }
+
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Jump. */
+ if (need_bnd_p)
+ fputs ("\tbnd jmp\t", asm_out_file);
+ else
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ /* Call. */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+ }
+}
+
+/* Output indirect branch via a call and return thunk. CALL_OP is
+ the branch target. XASM is the assembly template for CALL_OP.
+ Branch is a tail call if SIBCALL_P is true. A normal call is
+ converted to:
+
+ jmp L2
+ L1:
+ push CALL_OP
+ jmp __x86_indirect_thunk
+ L2:
+ call L1
+
+ and a tail call is converted to:
+
+ push CALL_OP
+ jmp __x86_indirect_thunk
+ */
+
+static void
+ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
+ bool sibcall_p)
+{
+ char thunk_name_buf[32];
+ char *thunk_name;
+ char push_buf[64];
+ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ int regno = -1;
+
+ if (cfun->machine->indirect_branch_type
+ != indirect_branch_thunk_inline)
+ {
+ if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+ {
+ if (need_bnd_p)
+ indirect_thunk_bnd_needed = true;
+ else
+ indirect_thunk_needed = true;
+ }
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ thunk_name = thunk_name_buf;
+ }
+ else
+ thunk_name = NULL;
+
+ snprintf (push_buf, sizeof (push_buf), "push{%c}\t%s",
+ TARGET_64BIT ? 'q' : 'l', xasm);
+
+ if (sibcall_p)
+ {
+ output_asm_insn (push_buf, &call_op);
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+ }
+ else
+ {
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Jump. */
+ if (need_bnd_p)
+ fputs ("\tbnd jmp\t", asm_out_file);
+ else
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ /* An external function may be called via GOT, instead of PLT. */
+ if (MEM_P (call_op))
+ {
+ struct ix86_address parts;
+ rtx addr = XEXP (call_op, 0);
+ if (ix86_decompose_address (addr, &parts)
+ && parts.base == stack_pointer_rtx)
+ {
+ /* Since call will adjust stack by -UNITS_PER_WORD,
+ we must convert "disp(stack, index, scale)" to
+ "disp+UNITS_PER_WORD(stack, index, scale)". */
+ if (parts.index)
+ {
+ addr = gen_rtx_MULT (Pmode, parts.index,
+ GEN_INT (parts.scale));
+ addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+ addr);
+ }
+ else
+ addr = stack_pointer_rtx;
+
+ rtx disp;
+ if (parts.disp != NULL_RTX)
+ disp = plus_constant (Pmode, parts.disp,
+ UNITS_PER_WORD);
+ else
+ disp = GEN_INT (UNITS_PER_WORD);
+
+ addr = gen_rtx_PLUS (Pmode, addr, disp);
+ call_op = gen_rtx_MEM (GET_MODE (call_op), addr);
+ }
+ }
+
+ output_asm_insn (push_buf, &call_op);
+
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ /* Call. */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+ }
+}
+
+/* Output indirect branch via a call and return thunk. CALL_OP is
+ the branch target. XASM is the assembly template for CALL_OP.
+ Branch is a tail call if SIBCALL_P is true. */
+
+static void
+ix86_output_indirect_branch (rtx call_op, const char *xasm,
+ bool sibcall_p)
+{
+ if (REG_P (call_op))
+ ix86_output_indirect_branch_via_reg (call_op, sibcall_p);
+ else
+ ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p);
+}
+/* Output indirect jump. CALL_OP is the jump target. Jump is a
+ function return if RET_P is true. */
+
+const char *
+ix86_output_indirect_jmp (rtx call_op, bool ret_p)
+{
+ if (cfun->machine->indirect_branch_type != indirect_branch_keep)
+ {
+ /* We can't have red-zone if this isn't a function return since
+ "call" in the indirect thunk pushes the return address onto
+ stack, destroying red-zone. */
+ if (!ret_p && ix86_red_zone_size != 0)
+ gcc_unreachable ();
+
+ ix86_output_indirect_branch (call_op, "%0", true);
+ return "";
+ }
+ else
+ return "%!jmp\t%A0";
+}
+
/* Output the assembly for a call instruction. */
const char *
ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
bool direct_p = constant_call_address_operand (call_op, VOIDmode);
+ bool output_indirect_p
+ = (!TARGET_SEH
+ && cfun->machine->indirect_branch_type != indirect_branch_keep);
bool seh_nop_p = false;
const char *xasm;
@@ -28165,10 +28726,21 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
if (ix86_nopic_noplt_attribute_p (call_op))
{
+ direct_p = false;
if (TARGET_64BIT)
- xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ else
+ xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ }
else
- xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}";
+ else
+ xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ }
}
else
xasm = "%!jmp\t%P0";
@@ -28178,9 +28750,17 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
else if (TARGET_SEH)
xasm = "%!rex.W jmp\t%A0";
else
- xasm = "%!jmp\t%A0";
+ {
+ if (output_indirect_p)
+ xasm = "%0";
+ else
+ xasm = "%!jmp\t%A0";
+ }
- output_asm_insn (xasm, &call_op);
+ if (output_indirect_p && !direct_p)
+ ix86_output_indirect_branch (call_op, xasm, true);
+ else
+ output_asm_insn (xasm, &call_op);
return "";
}
@@ -28218,18 +28798,37 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
if (ix86_nopic_noplt_attribute_p (call_op))
{
+ direct_p = false;
if (TARGET_64BIT)
- xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ else
+ xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ }
else
- xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}";
+ else
+ xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ }
}
else
xasm = "%!call\t%P0";
}
else
- xasm = "%!call\t%A0";
+ {
+ if (output_indirect_p)
+ xasm = "%0";
+ else
+ xasm = "%!call\t%A0";
+ }
- output_asm_insn (xasm, &call_op);
+ if (output_indirect_p && !direct_p)
+ ix86_output_indirect_branch (call_op, xasm, false);
+ else
+ output_asm_insn (xasm, &call_op);
if (seh_nop_p)
return "nop";
@@ -40387,7 +40986,7 @@ ix86_handle_struct_attribute (tree *node, tree name, tree, int,
}
static tree
-ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
+ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
@@ -40396,6 +40995,29 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
name);
*no_add_attrs = true;
}
+
+ if (is_attribute_p ("indirect_branch", name))
+ {
+ tree cst = TREE_VALUE (args);
+ if (TREE_CODE (cst) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qE attribute requires a string constant argument",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
+ {
+ warning (OPT_Wattributes,
+ "argument to %qE attribute is not "
+ "(keep|thunk|thunk-inline|thunk-extern)", name);
+ *no_add_attrs = true;
+ }
+ }
+
return NULL_TREE;
}
@@ -44842,6 +45464,8 @@ static const struct attribute_spec ix86_attribute_table[] =
ix86_handle_no_caller_saved_registers_attribute, NULL },
{ "naked", 0, 0, true, false, false, false,
ix86_handle_fndecl_attribute, NULL },
+ { "indirect_branch", 1, 1, true, false, false, false,
+ ix86_handle_fndecl_attribute, NULL },
/* End element. */
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 933f261ea66..3b939086112 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2570,6 +2570,13 @@ struct GTY(()) machine_function {
/* Function type. */
ENUM_BITFIELD(function_type) func_type : 2;
+ /* How to generate indirec branch. */
+ ENUM_BITFIELD(indirect_branch) indirect_branch_type : 3;
+
+ /* If true, the current function has local indirect jumps, like
+ "indirect_jump" or "tablejump". */
+ BOOL_BITFIELD has_local_indirect_jump : 1;
+
/* If true, the current function is a function specified with
the "interrupt" or "no_caller_saved_registers" attribute. */
BOOL_BITFIELD no_caller_saved_registers : 1;
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 3f587806407..1ba144da87e 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -12313,13 +12313,18 @@
{
if (TARGET_X32)
operands[0] = convert_memory_address (word_mode, operands[0]);
+ cfun->machine->has_local_indirect_jump = true;
})
(define_insn "*indirect_jump"
[(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))]
""
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], false);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
@@ -12362,14 +12367,19 @@
if (TARGET_X32)
operands[0] = convert_memory_address (word_mode, operands[0]);
+ cfun->machine->has_local_indirect_jump = true;
})
(define_insn "*tablejump_1"
[(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))
(use (label_ref (match_operand 1)))]
""
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], false);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
\f
@@ -13097,8 +13107,12 @@
[(simple_return)
(use (match_operand 0 "register_operand" "r"))]
"reload_completed"
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], true);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 09aaa97c2fc..59e5cc8e7e4 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -1021,3 +1021,23 @@ indirect jump.
mforce-indirect-call
Target Report Var(flag_force_indirect_call) Init(0)
Make all function calls indirect.
+
+mindirect-branch=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep)
+Convert indirect call and jump to call and return thunks.
+
+Enum
+Name(indirect_branch) Type(enum indirect_branch)
+Known indirect branch choices (for use with the -mindirect-branch= option):
+
+EnumValue
+Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
+
+EnumValue
+Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk)
+
+EnumValue
+Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
+
+EnumValue
+Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f3e4a63ab46..ddb6035be96 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5754,6 +5754,16 @@ Specify which floating-point unit to use. You must specify the
@code{target("fpmath=sse+387")} because the comma would separate
different options.
+@item indirect_branch("@var{choice}")
+@cindex @code{indirect_branch} function attribute, x86
+On x86 targets, the @code{indirect_branch} attribute causes the compiler
+to convert indirect call and jump with @var{choice}. @samp{keep}
+keeps indirect call and jump unmodified. @samp{thunk} converts indirect
+call and jump to call and return thunk. @samp{thunk-inline} converts
+indirect call and jump to inlined call and return thunk.
+@samp{thunk-extern} converts indirect call and jump to external call
+and return thunk provided in a separate object file.
+
@item nocf_check
@cindex @code{nocf_check} function attribute
The @code{nocf_check} attribute on a function is used to inform the
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a9449a86064..0d685c3576b 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1229,7 +1229,8 @@ See RS/6000 and PowerPC Options.
-mstack-protector-guard-reg=@var{reg} @gol
-mstack-protector-guard-offset=@var{offset} @gol
-mstack-protector-guard-symbol=@var{symbol} -mmitigate-rop @gol
--mgeneral-regs-only -mcall-ms2sysv-xlogues}
+-mgeneral-regs-only -mcall-ms2sysv-xlogues @gol
+-mindirect-branch=@var{choice}}
@emph{x86 Windows Options}
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
@@ -26838,6 +26839,17 @@ Generate code that uses only the general-purpose registers. This
prevents the compiler from using floating-point, vector, mask and bound
registers.
+@item -mindirect-branch=@var{choice}
+@opindex -mindirect-branch
+Convert indirect call and jump with @var{choice}. The default is
+@samp{keep}, which keeps indirect call and jump unmodified.
+@samp{thunk} converts indirect call and jump to call and return thunk.
+@samp{thunk-inline} converts indirect call and jump to inlined call
+and return thunk. @samp{thunk-extern} converts indirect call and jump
+to external call and return thunk provided in a separate object file.
+You can control this behavior for a specific function by using the
+function attribute @code{indirect_branch}. @xref{Function Attributes}.
+
@end table
These @samp{-m} switches are supported in addition to the above
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
new file mode 100644
index 00000000000..d983e1c3e26
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
new file mode 100644
index 00000000000..58f09b42d8a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
new file mode 100644
index 00000000000..f20d35c19b6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
new file mode 100644
index 00000000000..0eff8fb658a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
new file mode 100644
index 00000000000..a25b20dd808
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
new file mode 100644
index 00000000000..cff114a6c29
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
new file mode 100644
index 00000000000..afdb6007986
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
new file mode 100644
index 00000000000..d64d978b699
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+extern void male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk")));
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
new file mode 100644
index 00000000000..93067454d3d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk")))
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
new file mode 100644
index 00000000000..97744d65729
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+extern int male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk-inline")));
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
new file mode 100644
index 00000000000..bfce3ea5cb2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk-inline")))
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
new file mode 100644
index 00000000000..0833606046b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+extern int male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk-extern")));
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
new file mode 100644
index 00000000000..2eba0fbd9b2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk-extern")))
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
new file mode 100644
index 00000000000..f58427eae11
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
new file mode 100644
index 00000000000..564ed39547c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+__attribute__ ((indirect_branch("keep")))
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
new file mode 100644
index 00000000000..50fbee20a5a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+
+void (*dispatch) (char *);
+char buf[10];
+
+void
+foo (void)
+{
+ dispatch (buf);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
new file mode 100644
index 00000000000..2976e67adce
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+
+void (*dispatch) (char *);
+char buf[10];
+
+int
+foo (void)
+{
+ dispatch (buf);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
new file mode 100644
index 00000000000..da4bc98ef23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
@@ -0,0 +1,19 @@
+/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+
+void bar (char *);
+char buf[10];
+
+void
+foo (void)
+{
+ bar (buf);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
new file mode 100644
index 00000000000..c64d12ef989
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+
+void bar (char *);
+char buf[10];
+
+int
+foo (void)
+{
+ bar (buf);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-times "bnd call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
new file mode 100644
index 00000000000..49f27b49465
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
new file mode 100644
index 00000000000..a1e3eb6fc74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
new file mode 100644
index 00000000000..395634e7e5c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
new file mode 100644
index 00000000000..fd3f63379a1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
new file mode 100644
index 00000000000..ba2f92b6f34
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
new file mode 100644
index 00000000000..0c5a2d472c6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
new file mode 100644
index 00000000000..665252327aa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
new file mode 100644
index 00000000000..68c0ff713b3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
new file mode 100644
index 00000000000..e2da1fcb683
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
new file mode 100644
index 00000000000..244fec708d6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
new file mode 100644
index 00000000000..107ebe32f54
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
new file mode 100644
index 00000000000..17b04ef2229
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
new file mode 100644
index 00000000000..d9eb11285aa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \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 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
new file mode 100644
index 00000000000..d02b1dcb1b9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/5] x86: Add -mindirect-branch=
2018-01-14 12:53 ` H.J. Lu
@ 2018-01-14 15:11 ` H.J. Lu
0 siblings, 0 replies; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 15:11 UTC (permalink / raw)
To: Jan Hubicka, Uros Bizjak; +Cc: GCC Patches
[-- Attachment #1: Type: text/plain, Size: 2836 bytes --]
On Sun, Jan 14, 2018 at 4:52 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Sun, Jan 14, 2018 at 3:06 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>>> On 2018.01.14 at 11:46 +0100, Jan Hubicka wrote:
>>> > > gcc/
>>> > >
>>> > > * config/i386/i386-opts.h (indirect_branch): New.
>>> > > * config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
>>> > > * config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
>>> > > with local indirect jump when converting indirect call and jump.
>>> > > (ix86_set_indirect_branch_type): New.
>>> > > (ix86_set_current_function): Call ix86_set_indirect_branch_type.
>>> > > (indirectlabelno): New.
>>> > > (indirect_thunk_needed): Likewise.
>>> > > (indirect_thunk_bnd_needed): Likewise.
>>> > > (indirect_thunks_used): Likewise.
>>> > > (indirect_thunks_bnd_used): Likewise.
>>> > > (INDIRECT_LABEL): Likewise.
>>> > > (indirect_thunk_name): Likewise.
>>> > > (output_indirect_thunk): Likewise.
>>> > > (output_indirect_thunk_function): Likewise.
>>> > > (ix86_output_indirect_branch): Likewise.
>>> > > (ix86_output_indirect_jmp): Likewise.
>>> > > (ix86_code_end): Call output_indirect_thunk_function if needed.
>>> > > (ix86_output_call_insn): Call ix86_output_indirect_branch if
>>> > > needed.
>>> > > (ix86_handle_fndecl_attribute): Handle indirect_branch.
>>> > > (ix86_attribute_table): Add indirect_branch.
>>> > > * config/i386/i386.h (machine_function): Add indirect_branch_type
>>> > > and has_local_indirect_jump.
>>> > > * config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
>>> > > to true.
>>> > > (tablejump): Likewise.
>>> > > (*indirect_jump): Use ix86_output_indirect_jmp.
>>> > > (*tablejump_1): Likewise.
>>> > > (simple_return_indirect_internal): Likewise.
>>> > > * config/i386/i386.opt (mindirect-branch=): New option.
>>> > > (indirect_branch): New.
>>> > > (keep): Likewise.
>>> > > (thunk): Likewise.
>>> > > (thunk-inline): Likewise.
>>> > > (thunk-extern): Likewise.
>>> > > * doc/extend.texi: Document indirect_branch function attribute.
>>> > > * doc/invoke.texi: Document -mindirect-branch= option.
>>> > > +
>>> > > + /* Pause . */
>>> > > + fprintf (asm_out_file, "\tpause\n");
>>> >
>>> > OK, but please prepare incremental patches to choose between pause and lefence
>>> > as needed for AMD CPUs and check for large code model.
>>>
>>> Why not use both? That would make everybody happy, no?
>>>
>>> pause
>>> lfence
>>> jmp
>>
>> Yes, according to my understanding we want both pause and lefence by default
>> (it is expensive anyway) + command line option to control which one to use?
>>
>> Probably thus the first patch should default to both.
>
> Done. This is the patch I am checking in.
>
Here is the backport for GCC 7. OK for gcc-7-branch?
--
H.J.
[-- Attachment #2: 0001-x86-Add-mindirect-branch.patch --]
[-- Type: text/x-patch, Size: 72929 bytes --]
From 09f7c546376f7ed6770fc64f24aed77229f95f67 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 6 Jan 2018 22:29:55 -0800
Subject: [PATCH 1/5] x86: Add -mindirect-branch=
Add -mindirect-branch= option to convert indirect call and jump to call
and return thunks. The default is 'keep', which keeps indirect call and
jump unmodified. 'thunk' converts indirect call and jump to call and
return thunk. 'thunk-inline' converts indirect call and jump to inlined
call and return thunk. 'thunk-extern' converts indirect call and jump to
external call and return thunk provided in a separate object file. You
can control this behavior for a specific function by using the function
attribute indirect_branch.
2 kinds of thunks are geneated. Memory thunk where the function address
is at the top of the stack:
__x86_indirect_thunk:
call L2
L1:
pause
lfence
jmp L1
L2:
lea 8(%rsp), %rsp|lea 4(%esp), %esp
ret
Indirect jmp via memory, "jmp mem", is converted to
push memory
jmp __x86_indirect_thunk
Indirect call via memory, "call mem", is converted to
jmp L2
L1:
push [mem]
jmp __x86_indirect_thunk
L2:
call L1
Register thunk where the function address is in a register, reg:
__x86_indirect_thunk_reg:
call L2
L1:
pause
lfence
jmp L1
L2:
movq %reg, (%rsp)|movl %reg, (%esp)
ret
where reg is one of (r|e)ax, (r|e)dx, (r|e)cx, (r|e)bx, (r|e)si, (r|e)di,
(r|e)bp, r8, r9, r10, r11, r12, r13, r14 and r15.
Indirect jmp via register, "jmp reg", is converted to
jmp __x86_indirect_thunk_reg
Indirect call via register, "call reg", is converted to
call __x86_indirect_thunk_reg
gcc/
Backport from mainline
* config/i386/i386-opts.h (indirect_branch): New.
* config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise.
* config/i386/i386.c (ix86_using_red_zone): Disallow red-zone
with local indirect jump when converting indirect call and jump.
(ix86_set_indirect_branch_type): New.
(ix86_set_current_function): Call ix86_set_indirect_branch_type.
(indirectlabelno): New.
(indirect_thunk_needed): Likewise.
(indirect_thunk_bnd_needed): Likewise.
(indirect_thunks_used): Likewise.
(indirect_thunks_bnd_used): Likewise.
(INDIRECT_LABEL): Likewise.
(indirect_thunk_name): Likewise.
(output_indirect_thunk): Likewise.
(output_indirect_thunk_function): Likewise.
(ix86_output_indirect_branch): Likewise.
(ix86_output_indirect_jmp): Likewise.
(ix86_code_end): Call output_indirect_thunk_function if needed.
(ix86_output_call_insn): Call ix86_output_indirect_branch if
needed.
(ix86_handle_fndecl_attribute): Handle indirect_branch.
(ix86_attribute_table): Add indirect_branch.
* config/i386/i386.h (machine_function): Add indirect_branch_type
and has_local_indirect_jump.
* config/i386/i386.md (indirect_jump): Set has_local_indirect_jump
to true.
(tablejump): Likewise.
(*indirect_jump): Use ix86_output_indirect_jmp.
(*tablejump_1): Likewise.
(simple_return_indirect_internal): Likewise.
* config/i386/i386.opt (mindirect-branch=): New option.
(indirect_branch): New.
(keep): Likewise.
(thunk): Likewise.
(thunk-inline): Likewise.
(thunk-extern): Likewise.
* doc/extend.texi: Document indirect_branch function attribute.
* doc/invoke.texi: Document -mindirect-branch= option.
gcc/testsuite/
Backport from mainline
* gcc.target/i386/indirect-thunk-1.c: New test.
* gcc.target/i386/indirect-thunk-2.c: Likewise.
* gcc.target/i386/indirect-thunk-3.c: Likewise.
* gcc.target/i386/indirect-thunk-4.c: Likewise.
* gcc.target/i386/indirect-thunk-5.c: Likewise.
* gcc.target/i386/indirect-thunk-6.c: Likewise.
* gcc.target/i386/indirect-thunk-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-8.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
---
gcc/config/i386/i386-opts.h | 13 +
gcc/config/i386/i386-protos.h | 1 +
gcc/config/i386/i386.c | 648 ++++++++++++++++++++-
gcc/config/i386/i386.h | 7 +
gcc/config/i386/i386.md | 26 +-
gcc/config/i386/i386.opt | 20 +
gcc/doc/extend.texi | 10 +
gcc/doc/invoke.texi | 14 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 20 +
gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 20 +
gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 21 +
gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 21 +
gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 17 +
gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 18 +
gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 44 ++
.../gcc.target/i386/indirect-thunk-attr-1.c | 23 +
.../gcc.target/i386/indirect-thunk-attr-2.c | 21 +
.../gcc.target/i386/indirect-thunk-attr-3.c | 23 +
.../gcc.target/i386/indirect-thunk-attr-4.c | 22 +
.../gcc.target/i386/indirect-thunk-attr-5.c | 22 +
.../gcc.target/i386/indirect-thunk-attr-6.c | 21 +
.../gcc.target/i386/indirect-thunk-attr-7.c | 44 ++
.../gcc.target/i386/indirect-thunk-attr-8.c | 42 ++
.../gcc.target/i386/indirect-thunk-bnd-1.c | 20 +
.../gcc.target/i386/indirect-thunk-bnd-2.c | 21 +
.../gcc.target/i386/indirect-thunk-bnd-3.c | 19 +
.../gcc.target/i386/indirect-thunk-bnd-4.c | 20 +
.../gcc.target/i386/indirect-thunk-extern-1.c | 19 +
.../gcc.target/i386/indirect-thunk-extern-2.c | 19 +
.../gcc.target/i386/indirect-thunk-extern-3.c | 20 +
.../gcc.target/i386/indirect-thunk-extern-4.c | 20 +
.../gcc.target/i386/indirect-thunk-extern-5.c | 16 +
.../gcc.target/i386/indirect-thunk-extern-6.c | 17 +
.../gcc.target/i386/indirect-thunk-extern-7.c | 43 ++
.../gcc.target/i386/indirect-thunk-inline-1.c | 20 +
.../gcc.target/i386/indirect-thunk-inline-2.c | 20 +
.../gcc.target/i386/indirect-thunk-inline-3.c | 21 +
.../gcc.target/i386/indirect-thunk-inline-4.c | 21 +
.../gcc.target/i386/indirect-thunk-inline-5.c | 17 +
.../gcc.target/i386/indirect-thunk-inline-6.c | 18 +
.../gcc.target/i386/indirect-thunk-inline-7.c | 44 ++
41 files changed, 1494 insertions(+), 19 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h
index 542cd0f3d67..efcdc3b1a14 100644
--- a/gcc/config/i386/i386-opts.h
+++ b/gcc/config/i386/i386-opts.h
@@ -99,4 +99,17 @@ enum stack_protector_guard {
SSP_GLOBAL /* global canary */
};
+/* This is used to mitigate variant #2 of the speculative execution
+ vulnerabilities on x86 processors identified by CVE-2017-5715, aka
+ Spectre. They convert indirect branches and function returns to
+ call and return thunks to avoid speculative execution via indirect
+ call, jmp and ret. */
+enum indirect_branch {
+ indirect_branch_unset = 0,
+ indirect_branch_keep,
+ indirect_branch_thunk,
+ indirect_branch_thunk_inline,
+ indirect_branch_thunk_extern
+};
+
#endif
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index d2cccf14735..bcdd9872db9 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -313,6 +313,7 @@ extern enum attr_cpu ix86_schedule;
#endif
extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
+extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
enum machine_mode mode);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 986e6d79584..f1c58faa035 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -4212,12 +4212,23 @@ make_pass_stv (gcc::context *ctxt)
return new pass_stv (ctxt);
}
-/* Return true if a red-zone is in use. */
+/* Return true if a red-zone is in use. We can't use red-zone when
+ there are local indirect jumps, like "indirect_jump" or "tablejump",
+ which jumps to another place in the function, since "call" in the
+ indirect thunk pushes the return address onto stack, destroying
+ red-zone.
+
+ TODO: If we can reserve the first 2 WORDs, for PUSH and, another
+ for CALL, in red-zone, we can allow local indirect jumps with
+ indirect thunk. */
bool
ix86_using_red_zone (void)
{
- return TARGET_RED_ZONE && !TARGET_64BIT_MS_ABI;
+ return (TARGET_RED_ZONE
+ && !TARGET_64BIT_MS_ABI
+ && (!cfun->machine->has_local_indirect_jump
+ || cfun->machine->indirect_branch_type == indirect_branch_keep));
}
\f
/* Return a string that documents the current -m options. The caller is
@@ -7148,6 +7159,37 @@ ix86_set_func_type (tree fndecl)
}
}
+/* Set the indirect_branch_type field from the function FNDECL. */
+
+static void
+ix86_set_indirect_branch_type (tree fndecl)
+{
+ if (cfun->machine->indirect_branch_type == indirect_branch_unset)
+ {
+ tree attr = lookup_attribute ("indirect_branch",
+ DECL_ATTRIBUTES (fndecl));
+ if (attr != NULL)
+ {
+ tree args = TREE_VALUE (attr);
+ if (args == NULL)
+ gcc_unreachable ();
+ tree cst = TREE_VALUE (args);
+ if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_keep;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk_inline;
+ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0)
+ cfun->machine->indirect_branch_type = indirect_branch_thunk_extern;
+ else
+ gcc_unreachable ();
+ }
+ else
+ cfun->machine->indirect_branch_type = ix86_indirect_branch;
+ }
+}
+
/* Establish appropriate back-end context for processing the function
FNDECL. The argument might be NULL to indicate processing at top
level, outside of any function scope. */
@@ -7163,7 +7205,10 @@ ix86_set_current_function (tree fndecl)
one is extern inline and one isn't. Call ix86_set_func_type
to set the func_type field. */
if (fndecl != NULL_TREE)
- ix86_set_func_type (fndecl);
+ {
+ ix86_set_func_type (fndecl);
+ ix86_set_indirect_branch_type (fndecl);
+ }
return;
}
@@ -7183,6 +7228,7 @@ ix86_set_current_function (tree fndecl)
}
ix86_set_func_type (fndecl);
+ ix86_set_indirect_branch_type (fndecl);
tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
if (new_tree == NULL_TREE)
@@ -11920,6 +11966,220 @@ ix86_setup_frame_addresses (void)
# endif
#endif
+/* Label count for call and return thunks. It is used to make unique
+ labels in call and return thunks. */
+static int indirectlabelno;
+
+/* True if call and return thunk functions are needed. */
+static bool indirect_thunk_needed = false;
+/* True if call and return thunk functions with the BND prefix are
+ needed. */
+static bool indirect_thunk_bnd_needed = false;
+
+/* Bit masks of integer registers, which contain branch target, used
+ by call and return thunks functions. */
+static int indirect_thunks_used;
+/* Bit masks of integer registers, which contain branch target, used
+ by call and return thunks functions with the BND prefix. */
+static int indirect_thunks_bnd_used;
+
+#ifndef INDIRECT_LABEL
+# define INDIRECT_LABEL "LIND"
+#endif
+
+/* Fills in the label name that should be used for the indirect thunk. */
+
+static void
+indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
+{
+ if (USE_HIDDEN_LINKONCE)
+ {
+ const char *bnd = need_bnd_p ? "_bnd" : "";
+ if (regno >= 0)
+ {
+ const char *reg_prefix;
+ if (LEGACY_INT_REGNO_P (regno))
+ reg_prefix = TARGET_64BIT ? "r" : "e";
+ else
+ reg_prefix = "";
+ sprintf (name, "__x86_indirect_thunk%s_%s%s",
+ bnd, reg_prefix, reg_names[regno]);
+ }
+ else
+ sprintf (name, "__x86_indirect_thunk%s", bnd);
+ }
+ else
+ {
+ if (regno >= 0)
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITBR", regno);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITR", regno);
+ }
+ else
+ {
+ if (need_bnd_p)
+ ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
+ }
+ }
+}
+
+/* Output a call and return thunk for indirect branch. If BND_P is
+ true, the BND prefix is needed. If REGNO != -1, the function
+ address is in REGNO and the call and return thunk looks like:
+
+ call L2
+ L1:
+ pause
+ jmp L1
+ L2:
+ mov %REG, (%sp)
+ ret
+
+ Otherwise, the function address is on the top of stack and the
+ call and return thunk looks like:
+
+ call L2
+ L1:
+ pause
+ jmp L1
+ L2:
+ lea WORD_SIZE(%sp), %sp
+ ret
+ */
+
+static void
+output_indirect_thunk (bool need_bnd_p, int regno)
+{
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Call */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ /* Pause + lfence. */
+ fprintf (asm_out_file, "\tpause\n\tlfence\n");
+
+ /* Jump. */
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ if (regno >= 0)
+ {
+ /* MOV. */
+ rtx xops[2];
+ xops[0] = gen_rtx_MEM (word_mode, stack_pointer_rtx);
+ xops[1] = gen_rtx_REG (word_mode, regno);
+ output_asm_insn ("mov\t{%1, %0|%0, %1}", xops);
+ }
+ else
+ {
+ /* LEA. */
+ rtx xops[2];
+ xops[0] = stack_pointer_rtx;
+ xops[1] = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD);
+ output_asm_insn ("lea\t{%E1, %0|%0, %E1}", xops);
+ }
+
+ if (need_bnd_p)
+ fputs ("\tbnd ret\n", asm_out_file);
+ else
+ fputs ("\tret\n", asm_out_file);
+}
+
+/* Output a funtion with a call and return thunk for indirect branch.
+ If BND_P is true, the BND prefix is needed. If REGNO != -1, the
+ function address is in REGNO. Otherwise, the function address is
+ on the top of stack. */
+
+static void
+output_indirect_thunk_function (bool need_bnd_p, int regno)
+{
+ char name[32];
+ tree decl;
+
+ /* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd. */
+ indirect_thunk_name (name, regno, need_bnd_p);
+ decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+ get_identifier (name),
+ build_function_type_list (void_type_node, NULL_TREE));
+ DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
+ NULL_TREE, void_type_node);
+ TREE_PUBLIC (decl) = 1;
+ TREE_STATIC (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+
+#if TARGET_MACHO
+ if (TARGET_MACHO)
+ {
+ switch_to_section (darwin_sections[picbase_thunk_section]);
+ fputs ("\t.weak_definition\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n\t.private_extern\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ putc ('\n', asm_out_file);
+ ASM_OUTPUT_LABEL (asm_out_file, name);
+ DECL_WEAK (decl) = 1;
+ }
+ else
+#endif
+ if (USE_HIDDEN_LINKONCE)
+ {
+ cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl));
+
+ targetm.asm_out.unique_section (decl, 0);
+ switch_to_section (get_named_section (decl, NULL, 0));
+
+ targetm.asm_out.globalize_label (asm_out_file, name);
+ fputs ("\t.hidden\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ putc ('\n', asm_out_file);
+ ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl);
+ }
+ else
+ {
+ switch_to_section (text_section);
+ ASM_OUTPUT_LABEL (asm_out_file, name);
+ }
+
+ DECL_INITIAL (decl) = make_node (BLOCK);
+ current_function_decl = decl;
+ allocate_struct_function (decl, false);
+ init_function_start (decl);
+ /* We're about to hide the function body from callees of final_* by
+ emitting it directly; tell them we're a thunk, if they care. */
+ cfun->is_thunk = true;
+ first_function_block_is_cold = false;
+ /* Make sure unwind info is emitted for the thunk if needed. */
+ final_start_function (emit_barrier (), asm_out_file, 1);
+
+ output_indirect_thunk (need_bnd_p, regno);
+
+ final_end_function ();
+ init_insn_lengths ();
+ free_after_compilation (cfun);
+ set_cfun (NULL);
+ current_function_decl = NULL;
+}
+
static int pic_labels_used;
/* Fills in the label name that should be used for a pc thunk for
@@ -11946,11 +12206,32 @@ ix86_code_end (void)
rtx xops[2];
int regno;
+ if (indirect_thunk_needed)
+ output_indirect_thunk_function (false, -1);
+ if (indirect_thunk_bnd_needed)
+ output_indirect_thunk_function (true, -1);
+
+ for (regno = FIRST_REX_INT_REG; regno <= LAST_REX_INT_REG; regno++)
+ {
+ int i = regno - FIRST_REX_INT_REG + LAST_INT_REG + 1;
+ if ((indirect_thunks_used & (1 << i)))
+ output_indirect_thunk_function (false, regno);
+
+ if ((indirect_thunks_bnd_used & (1 << i)))
+ output_indirect_thunk_function (true, regno);
+ }
+
for (regno = AX_REG; regno <= SP_REG; regno++)
{
char name[32];
tree decl;
+ if ((indirect_thunks_used & (1 << regno)))
+ output_indirect_thunk_function (false, regno);
+
+ if ((indirect_thunks_bnd_used & (1 << regno)))
+ output_indirect_thunk_function (true, regno);
+
if (!(pic_labels_used & (1 << regno)))
continue;
@@ -28446,12 +28727,292 @@ ix86_nopic_noplt_attribute_p (rtx call_op)
return false;
}
+/* Output indirect branch via a call and return thunk. CALL_OP is a
+ register which contains the branch target. XASM is the assembly
+ template for CALL_OP. Branch is a tail call if SIBCALL_P is true.
+ A normal call is converted to:
+
+ call __x86_indirect_thunk_reg
+
+ and a tail call is converted to:
+
+ jmp __x86_indirect_thunk_reg
+ */
+
+static void
+ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
+{
+ char thunk_name_buf[32];
+ char *thunk_name;
+ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ int regno = REGNO (call_op);
+
+ if (cfun->machine->indirect_branch_type
+ != indirect_branch_thunk_inline)
+ {
+ if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+ {
+ int i = regno;
+ if (i >= FIRST_REX_INT_REG)
+ i -= (FIRST_REX_INT_REG - LAST_INT_REG - 1);
+ if (need_bnd_p)
+ indirect_thunks_bnd_used |= 1 << i;
+ else
+ indirect_thunks_used |= 1 << i;
+ }
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ thunk_name = thunk_name_buf;
+ }
+ else
+ thunk_name = NULL;
+
+ if (sibcall_p)
+ {
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+ }
+ else
+ {
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd call\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tcall\t%s\n", thunk_name);
+ return;
+ }
+
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Jump. */
+ if (need_bnd_p)
+ fputs ("\tbnd jmp\t", asm_out_file);
+ else
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ /* Call. */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+ }
+}
+
+/* Output indirect branch via a call and return thunk. CALL_OP is
+ the branch target. XASM is the assembly template for CALL_OP.
+ Branch is a tail call if SIBCALL_P is true. A normal call is
+ converted to:
+
+ jmp L2
+ L1:
+ push CALL_OP
+ jmp __x86_indirect_thunk
+ L2:
+ call L1
+
+ and a tail call is converted to:
+
+ push CALL_OP
+ jmp __x86_indirect_thunk
+ */
+
+static void
+ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
+ bool sibcall_p)
+{
+ char thunk_name_buf[32];
+ char *thunk_name;
+ char push_buf[64];
+ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ int regno = -1;
+
+ if (cfun->machine->indirect_branch_type
+ != indirect_branch_thunk_inline)
+ {
+ if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+ {
+ if (need_bnd_p)
+ indirect_thunk_bnd_needed = true;
+ else
+ indirect_thunk_needed = true;
+ }
+ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p);
+ thunk_name = thunk_name_buf;
+ }
+ else
+ thunk_name = NULL;
+
+ snprintf (push_buf, sizeof (push_buf), "push{%c}\t%s",
+ TARGET_64BIT ? 'q' : 'l', xasm);
+
+ if (sibcall_p)
+ {
+ output_asm_insn (push_buf, &call_op);
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+ }
+ else
+ {
+ char indirectlabel1[32];
+ char indirectlabel2[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2,
+ INDIRECT_LABEL,
+ indirectlabelno++);
+
+ /* Jump. */
+ if (need_bnd_p)
+ fputs ("\tbnd jmp\t", asm_out_file);
+ else
+ fputs ("\tjmp\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel2);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+
+ /* An external function may be called via GOT, instead of PLT. */
+ if (MEM_P (call_op))
+ {
+ struct ix86_address parts;
+ rtx addr = XEXP (call_op, 0);
+ if (ix86_decompose_address (addr, &parts)
+ && parts.base == stack_pointer_rtx)
+ {
+ /* Since call will adjust stack by -UNITS_PER_WORD,
+ we must convert "disp(stack, index, scale)" to
+ "disp+UNITS_PER_WORD(stack, index, scale)". */
+ if (parts.index)
+ {
+ addr = gen_rtx_MULT (Pmode, parts.index,
+ GEN_INT (parts.scale));
+ addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+ addr);
+ }
+ else
+ addr = stack_pointer_rtx;
+
+ rtx disp;
+ if (parts.disp != NULL_RTX)
+ disp = plus_constant (Pmode, parts.disp,
+ UNITS_PER_WORD);
+ else
+ disp = GEN_INT (UNITS_PER_WORD);
+
+ addr = gen_rtx_PLUS (Pmode, addr, disp);
+ call_op = gen_rtx_MEM (GET_MODE (call_op), addr);
+ }
+ }
+
+ output_asm_insn (push_buf, &call_op);
+
+ if (thunk_name != NULL)
+ {
+ if (need_bnd_p)
+ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+ else
+ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+ }
+ else
+ output_indirect_thunk (need_bnd_p, regno);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+
+ /* Call. */
+ if (need_bnd_p)
+ fputs ("\tbnd call\t", asm_out_file);
+ else
+ fputs ("\tcall\t", asm_out_file);
+ assemble_name_raw (asm_out_file, indirectlabel1);
+ fputc ('\n', asm_out_file);
+ }
+}
+
+/* Output indirect branch via a call and return thunk. CALL_OP is
+ the branch target. XASM is the assembly template for CALL_OP.
+ Branch is a tail call if SIBCALL_P is true. */
+
+static void
+ix86_output_indirect_branch (rtx call_op, const char *xasm,
+ bool sibcall_p)
+{
+ if (REG_P (call_op))
+ ix86_output_indirect_branch_via_reg (call_op, sibcall_p);
+ else
+ ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p);
+}
+/* Output indirect jump. CALL_OP is the jump target. Jump is a
+ function return if RET_P is true. */
+
+const char *
+ix86_output_indirect_jmp (rtx call_op, bool ret_p)
+{
+ if (cfun->machine->indirect_branch_type != indirect_branch_keep)
+ {
+ /* We can't have red-zone if this isn't a function return since
+ "call" in the indirect thunk pushes the return address onto
+ stack, destroying red-zone. */
+ if (!ret_p && ix86_red_zone_size != 0)
+ gcc_unreachable ();
+
+ ix86_output_indirect_branch (call_op, "%0", true);
+ return "";
+ }
+ else
+ return "%!jmp\t%A0";
+}
+
/* Output the assembly for a call instruction. */
const char *
ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
bool direct_p = constant_call_address_operand (call_op, VOIDmode);
+ bool output_indirect_p
+ = (!TARGET_SEH
+ && cfun->machine->indirect_branch_type != indirect_branch_keep);
bool seh_nop_p = false;
const char *xasm;
@@ -28461,10 +29022,21 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
if (ix86_nopic_noplt_attribute_p (call_op))
{
+ direct_p = false;
if (TARGET_64BIT)
- xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ else
+ xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ }
else
- xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}";
+ else
+ xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ }
}
else
xasm = "%!jmp\t%P0";
@@ -28474,9 +29046,17 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
else if (TARGET_SEH)
xasm = "%!rex.W jmp\t%A0";
else
- xasm = "%!jmp\t%A0";
+ {
+ if (output_indirect_p)
+ xasm = "%0";
+ else
+ xasm = "%!jmp\t%A0";
+ }
- output_asm_insn (xasm, &call_op);
+ if (output_indirect_p && !direct_p)
+ ix86_output_indirect_branch (call_op, xasm, true);
+ else
+ output_asm_insn (xasm, &call_op);
return "";
}
@@ -28514,18 +29094,37 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
{
if (ix86_nopic_noplt_attribute_p (call_op))
{
+ direct_p = false;
if (TARGET_64BIT)
- xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ else
+ xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}";
+ }
else
- xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ {
+ if (output_indirect_p)
+ xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}";
+ else
+ xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}";
+ }
}
else
xasm = "%!call\t%P0";
}
else
- xasm = "%!call\t%A0";
+ {
+ if (output_indirect_p)
+ xasm = "%0";
+ else
+ xasm = "%!call\t%A0";
+ }
- output_asm_insn (xasm, &call_op);
+ if (output_indirect_p && !direct_p)
+ ix86_output_indirect_branch (call_op, xasm, false);
+ else
+ output_asm_insn (xasm, &call_op);
if (seh_nop_p)
return "nop";
@@ -41444,7 +42043,7 @@ ix86_handle_struct_attribute (tree *node, tree name, tree, int,
}
static tree
-ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
+ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
@@ -41453,6 +42052,29 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
name);
*no_add_attrs = true;
}
+
+ if (is_attribute_p ("indirect_branch", name))
+ {
+ tree cst = TREE_VALUE (args);
+ if (TREE_CODE (cst) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qE attribute requires a string constant argument",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
+ && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
+ {
+ warning (OPT_Wattributes,
+ "argument to %qE attribute is not "
+ "(keep|thunk|thunk-inline|thunk-extern)", name);
+ *no_add_attrs = true;
+ }
+ }
+
return NULL_TREE;
}
@@ -45761,6 +46383,8 @@ static const struct attribute_spec ix86_attribute_table[] =
ix86_handle_interrupt_attribute, false },
{ "no_caller_saved_registers", 0, 0, false, true, true,
ix86_handle_no_caller_saved_registers_attribute, false },
+ { "indirect_branch", 1, 1, true, false, false,
+ ix86_handle_fndecl_attribute, false },
/* End element. */
{ NULL, 0, 0, false, false, false, NULL, false }
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index f9b91286a01..9d2209e605b 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2609,6 +2609,13 @@ struct GTY(()) machine_function {
/* Function type. */
ENUM_BITFIELD(function_type) func_type : 2;
+ /* How to generate indirec branch. */
+ ENUM_BITFIELD(indirect_branch) indirect_branch_type : 3;
+
+ /* If true, the current function has local indirect jumps, like
+ "indirect_jump" or "tablejump". */
+ BOOL_BITFIELD has_local_indirect_jump : 1;
+
/* If true, the current function is a function specified with
the "interrupt" or "no_caller_saved_registers" attribute. */
BOOL_BITFIELD no_caller_saved_registers : 1;
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index dbe88f40c8f..cd2e73cf9d3 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -11627,13 +11627,18 @@
{
if (TARGET_X32)
operands[0] = convert_memory_address (word_mode, operands[0]);
+ cfun->machine->has_local_indirect_jump = true;
})
(define_insn "*indirect_jump"
[(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))]
""
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], false);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
@@ -11676,14 +11681,19 @@
if (TARGET_X32)
operands[0] = convert_memory_address (word_mode, operands[0]);
+ cfun->machine->has_local_indirect_jump = true;
})
(define_insn "*tablejump_1"
[(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))
(use (label_ref (match_operand 1)))]
""
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], false);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
\f
@@ -12354,8 +12364,12 @@
[(simple_return)
(use (match_operand:SI 0 "register_operand" "r"))]
"reload_completed"
- "%!jmp\t%A0"
- [(set_attr "type" "ibr")
+ "* return ix86_output_indirect_jmp (operands[0], true);"
+ [(set (attr "type")
+ (if_then_else (match_test "(cfun->machine->indirect_branch_type
+ != indirect_branch_keep)")
+ (const_string "multi")
+ (const_string "ibr")))
(set_attr "length_immediate" "0")
(set_attr "maybe_prefix_bnd" "1")])
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 9384e29b1de..c076d9c70ab 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -927,3 +927,23 @@ Attempt to avoid generating instruction sequences containing ret bytes.
mgeneral-regs-only
Target Report RejectNegative Mask(GENERAL_REGS_ONLY) Var(ix86_target_flags) Save
Generate code which uses only the general registers.
+
+mindirect-branch=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep)
+Convert indirect call and jump to call and return thunks.
+
+Enum
+Name(indirect_branch) Type(enum indirect_branch)
+Known indirect branch choices (for use with the -mindirect-branch= option):
+
+EnumValue
+Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
+
+EnumValue
+Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk)
+
+EnumValue
+Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
+
+EnumValue
+Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ba309d01a9b..935381da6fa 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5540,6 +5540,16 @@ Specify which floating-point unit to use. You must specify the
@code{target("fpmath=sse,387")} option as
@code{target("fpmath=sse+387")} because the comma would separate
different options.
+
+@item indirect_branch("@var{choice}")
+@cindex @code{indirect_branch} function attribute, x86
+On x86 targets, the @code{indirect_branch} attribute causes the compiler
+to convert indirect call and jump with @var{choice}. @samp{keep}
+keeps indirect call and jump unmodified. @samp{thunk} converts indirect
+call and jump to call and return thunk. @samp{thunk-inline} converts
+indirect call and jump to inlined call and return thunk.
+@samp{thunk-extern} converts indirect call and jump to external call
+and return thunk provided in a separate object file.
@end table
On the x86, the inliner does not inline a
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 7311c10a754..4979c8c939d 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1210,7 +1210,8 @@ See RS/6000 and PowerPC Options.
-msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol
-mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol
-malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol
--mmitigate-rop -mgeneral-regs-only}
+-mmitigate-rop -mgeneral-regs-only @gol
+-mindirect-branch=@var{choice}}
@emph{x86 Windows Options}
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
@@ -25686,6 +25687,17 @@ Generate code that uses only the general-purpose registers. This
prevents the compiler from using floating-point, vector, mask and bound
registers.
+@item -mindirect-branch=@var{choice}
+@opindex -mindirect-branch
+Convert indirect call and jump with @var{choice}. The default is
+@samp{keep}, which keeps indirect call and jump unmodified.
+@samp{thunk} converts indirect call and jump to call and return thunk.
+@samp{thunk-inline} converts indirect call and jump to inlined call
+and return thunk. @samp{thunk-extern} converts indirect call and jump
+to external call and return thunk provided in a separate object file.
+You can control this behavior for a specific function by using the
+function attribute @code{indirect_branch}. @xref{Function Attributes}.
+
@end table
These @samp{-m} switches are supported in addition to the above
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
new file mode 100644
index 00000000000..d983e1c3e26
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
new file mode 100644
index 00000000000..58f09b42d8a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
new file mode 100644
index 00000000000..f20d35c19b6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
new file mode 100644
index 00000000000..0eff8fb658a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
new file mode 100644
index 00000000000..a25b20dd808
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
new file mode 100644
index 00000000000..cff114a6c29
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
new file mode 100644
index 00000000000..afdb6007986
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
new file mode 100644
index 00000000000..d64d978b699
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+extern void male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk")));
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
new file mode 100644
index 00000000000..93067454d3d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk")))
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
new file mode 100644
index 00000000000..97744d65729
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+extern int male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk-inline")));
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
new file mode 100644
index 00000000000..bfce3ea5cb2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk-inline")))
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
new file mode 100644
index 00000000000..0833606046b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+extern int male_indirect_jump (long)
+ __attribute__ ((indirect_branch("thunk-extern")));
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
new file mode 100644
index 00000000000..2eba0fbd9b2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk-extern")))
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
new file mode 100644
index 00000000000..f58427eae11
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
new file mode 100644
index 00000000000..564ed39547c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+__attribute__ ((indirect_branch("keep")))
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
new file mode 100644
index 00000000000..50fbee20a5a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+
+void (*dispatch) (char *);
+char buf[10];
+
+void
+foo (void)
+{
+ dispatch (buf);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
new file mode 100644
index 00000000000..2976e67adce
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+
+void (*dispatch) (char *);
+char buf[10];
+
+int
+foo (void)
+{
+ dispatch (buf);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
new file mode 100644
index 00000000000..da4bc98ef23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
@@ -0,0 +1,19 @@
+/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+
+void bar (char *);
+char buf[10];
+
+void
+foo (void)
+{
+ bar (buf);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
new file mode 100644
index 00000000000..c64d12ef989
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+
+void bar (char *);
+char buf[10];
+
+int
+foo (void)
+{
+ bar (buf);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-times "bnd call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler "bnd ret" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
new file mode 100644
index 00000000000..49f27b49465
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
new file mode 100644
index 00000000000..a1e3eb6fc74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
new file mode 100644
index 00000000000..395634e7e5c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
new file mode 100644
index 00000000000..fd3f63379a1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
new file mode 100644
index 00000000000..ba2f92b6f34
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
new file mode 100644
index 00000000000..0c5a2d472c6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
new file mode 100644
index 00000000000..665252327aa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */
+/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
new file mode 100644
index 00000000000..68c0ff713b3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
new file mode 100644
index 00000000000..e2da1fcb683
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
new file mode 100644
index 00000000000..244fec708d6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
new file mode 100644
index 00000000000..107ebe32f54
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+ dispatch[offset](offset);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */
+/* { dg-final { scan-assembler-times {\tpause} 1 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
new file mode 100644
index 00000000000..17b04ef2229
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
new file mode 100644
index 00000000000..d9eb11285aa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \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 } } */
+/* { dg-final { scan-assembler-times {\tlfence} 1 } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
new file mode 100644
index 00000000000..d02b1dcb1b9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+void func0 (void);
+void func1 (void);
+void func2 (void);
+void func3 (void);
+void func4 (void);
+void func4 (void);
+void func5 (void);
+
+void
+bar (int i)
+{
+ switch (i)
+ {
+ default:
+ func0 ();
+ break;
+ case 1:
+ func1 ();
+ break;
+ case 2:
+ func2 ();
+ break;
+ case 3:
+ func3 ();
+ break;
+ case 4:
+ func4 ();
+ break;
+ case 5:
+ func5 ();
+ break;
+ }
+}
+
+/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/5] x86: Add -mindirect-branch=
2018-01-14 11:21 ` Jan Hubicka
2018-01-14 12:53 ` H.J. Lu
@ 2018-01-14 20:21 ` David Woodhouse
1 sibling, 0 replies; 120+ messages in thread
From: David Woodhouse @ 2018-01-14 20:21 UTC (permalink / raw)
To: Jan Hubicka, Markus Trippelsdorf; +Cc: H.J. Lu, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 379 bytes --]
On Sun, 2018-01-14 at 12:06 +0100, Jan Hubicka wrote:
>
> Yes, according to my understanding we want both pause and lefence by default
> (it is expensive anyway) + command line option to control which one to use?
>
> Probably thus the first patch should default to both.
In the kernel we're going to just do both. I'm not sure I see the merit
in a command line option.
[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5213 bytes --]
^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 3/5] x86: Add -mindirect-branch-register
2018-01-14 3:37 [PATCH 0/5] x86: CVE-2017-5715, aka Spectre H.J. Lu
` (2 preceding siblings ...)
2018-01-14 3:37 ` [PATCH 1/5] x86: Add -mindirect-branch= H.J. Lu
@ 2018-01-14 3:37 ` H.J. Lu
2018-01-14 10:47 ` Jan Hubicka
2018-01-14 3:40 ` [PATCH 4/5] x86: Add 'V' register operand modifier H.J. Lu
2018-01-14 8:48 ` [PATCH 0/5] x86: CVE-2017-5715, aka Spectre Kumar, Venkataramanan
5 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 3:37 UTC (permalink / raw)
To: gcc-patches
Add -mindirect-branch-register to force indirect branch via register.
This is implemented by disabling patterns of indirect branch via memory,
similar to TARGET_X32.
-mindirect-branch= and -mfunction-return= tests are updated with
-mno-indirect-branch-register to avoid false test failures when
-mindirect-branch-register is added to RUNTESTFLAGS for "make check".
gcc/
* config/i386/constraints.md (Bs): Disallow memory operand for
-mindirect-branch-register.
(Bw): Likewise.
* config/i386/predicates.md (indirect_branch_operand): Likewise.
(GOT_memory_operand): Likewise.
(call_insn_operand): Likewise.
(sibcall_insn_operand): Likewise.
(GOT32_symbol_operand): Likewise.
* config/i386/i386.md (indirect_jump): Call convert_memory_address
for -mindirect-branch-register.
(tablejump): Likewise.
(*sibcall_memory): Likewise.
(*sibcall_value_memory): Likewise.
Disallow peepholes of indirect call and jump via memory for
-mindirect-branch-register.
(*call_pop): Replace m with Bw.
(*call_value_pop): Likewise.
(*sibcall_pop_memory): Replace m with Bs.
* config/i386/i386.opt (mindirect-branch-register): New option.
* doc/invoke.texi: Document -mindirect-branch-register option.
gcc/testsuite/
* gcc.target/i386/indirect-thunk-1.c (dg-options): Add
-mno-indirect-branch-register.
* gcc.target/i386/indirect-thunk-2.c: Likewise.
* gcc.target/i386/indirect-thunk-3.c: Likewise.
* gcc.target/i386/indirect-thunk-4.c: Likewise.
* gcc.target/i386/indirect-thunk-5.c: Likewise.
* gcc.target/i386/indirect-thunk-6.c: Likewise.
* gcc.target/i386/indirect-thunk-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
* gcc.target/i386/ret-thunk-10.c: Likewise.
* gcc.target/i386/ret-thunk-11.c: Likewise.
* gcc.target/i386/ret-thunk-12.c: Likewise.
* gcc.target/i386/ret-thunk-13.c: Likewise.
* gcc.target/i386/ret-thunk-14.c: Likewise.
* gcc.target/i386/ret-thunk-15.c: Likewise.
* gcc.target/i386/ret-thunk-9.c: Likewise.
* gcc.target/i386/indirect-thunk-register-1.c: New test.
* gcc.target/i386/indirect-thunk-register-2.c: Likewise.
* gcc.target/i386/indirect-thunk-register-3.c: Likewise.
---
gcc/config/i386/constraints.md | 12 +++++---
gcc/config/i386/i386.md | 34 ++++++++++++++--------
gcc/config/i386/i386.opt | 4 +++
gcc/config/i386/predicates.md | 21 ++++++++-----
gcc/doc/invoke.texi | 7 ++++-
gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-register-1.c | 22 ++++++++++++++
.../gcc.target/i386/indirect-thunk-register-2.c | 20 +++++++++++++
.../gcc.target/i386/indirect-thunk-register-3.c | 19 ++++++++++++
gcc/testsuite/gcc.target/i386/ret-thunk-10.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-11.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-12.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-13.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-14.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-15.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-9.c | 2 +-
47 files changed, 154 insertions(+), 63 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
diff --git a/gcc/config/i386/constraints.md b/gcc/config/i386/constraints.md
index 5f6c6d65332..5592c43073e 100644
--- a/gcc/config/i386/constraints.md
+++ b/gcc/config/i386/constraints.md
@@ -225,16 +225,20 @@
(define_constraint "Bs"
"@internal Sibcall memory operand."
- (ior (and (not (match_test "TARGET_X32"))
+ (ior (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "sibcall_memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (match_test "TARGET_X32 && Pmode == DImode
+ && !ix86_indirect_branch_thunk_register")
(match_operand 0 "GOT_memory_operand"))))
(define_constraint "Bw"
"@internal Call memory operand."
- (ior (and (not (match_test "TARGET_X32"))
+ (ior (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (match_test "TARGET_X32 && Pmode == DImode
+ && !ix86_indirect_branch_thunk_register")
(match_operand 0 "GOT_memory_operand"))))
(define_constraint "Bz"
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 7e21c486d19..fff73fe18e0 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -12311,7 +12311,7 @@
[(set (pc) (match_operand 0 "indirect_branch_operand"))]
""
{
- if (TARGET_X32)
+ if (TARGET_X32 || ix86_indirect_branch_thunk_register)
operands[0] = convert_memory_address (word_mode, operands[0]);
cfun->machine->has_local_indirect_jump = true;
})
@@ -12365,7 +12365,7 @@
OPTAB_DIRECT);
}
- if (TARGET_X32)
+ if (TARGET_X32 || ix86_indirect_branch_thunk_register)
operands[0] = convert_memory_address (word_mode, operands[0]);
cfun->machine->has_local_indirect_jump = true;
})
@@ -12614,7 +12614,7 @@
[(call (mem:QI (match_operand:W 0 "memory_operand" "m"))
(match_operand 1))
(unspec [(const_int 0)] UNSPEC_PEEPSIB)]
- "!TARGET_X32"
+ "!TARGET_X32 && !ix86_indirect_branch_thunk_register"
"* return ix86_output_call_insn (insn, operands[0]);"
[(set_attr "type" "call")])
@@ -12623,7 +12623,9 @@
(match_operand:W 1 "memory_operand"))
(call (mem:QI (match_dup 0))
(match_operand 3))]
- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1))
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && SIBLING_CALL_P (peep2_next_insn (1))
&& !reg_mentioned_p (operands[0],
CALL_INSN_FUNCTION_USAGE (peep2_next_insn (1)))"
[(parallel [(call (mem:QI (match_dup 1))
@@ -12636,7 +12638,9 @@
(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
(call (mem:QI (match_dup 0))
(match_operand 3))]
- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2))
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && SIBLING_CALL_P (peep2_next_insn (2))
&& !reg_mentioned_p (operands[0],
CALL_INSN_FUNCTION_USAGE (peep2_next_insn (2)))"
[(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
@@ -12658,7 +12662,7 @@
})
(define_insn "*call_pop"
- [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lmBz"))
+ [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lBwBz"))
(match_operand 1))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
@@ -12678,7 +12682,7 @@
[(set_attr "type" "call")])
(define_insn "*sibcall_pop_memory"
- [(call (mem:QI (match_operand:SI 0 "memory_operand" "m"))
+ [(call (mem:QI (match_operand:SI 0 "memory_operand" "Bs"))
(match_operand 1))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
@@ -12732,7 +12736,9 @@
[(set (match_operand:W 0 "register_operand")
(match_operand:W 1 "memory_operand"))
(set (pc) (match_dup 0))]
- "!TARGET_X32 && peep2_reg_dead_p (2, operands[0])"
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && peep2_reg_dead_p (2, operands[0])"
[(set (pc) (match_dup 1))])
;; Call subroutine, returning value in operand 0
@@ -12813,7 +12819,7 @@
(call (mem:QI (match_operand:W 1 "memory_operand" "m"))
(match_operand 2)))
(unspec [(const_int 0)] UNSPEC_PEEPSIB)]
- "!TARGET_X32"
+ "!TARGET_X32 && !ix86_indirect_branch_thunk_register"
"* return ix86_output_call_insn (insn, operands[1]);"
[(set_attr "type" "callv")])
@@ -12823,7 +12829,9 @@
(set (match_operand 2)
(call (mem:QI (match_dup 0))
(match_operand 3)))]
- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1))
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && SIBLING_CALL_P (peep2_next_insn (1))
&& !reg_mentioned_p (operands[0],
CALL_INSN_FUNCTION_USAGE (peep2_next_insn (1)))"
[(parallel [(set (match_dup 2)
@@ -12838,7 +12846,9 @@
(set (match_operand 2)
(call (mem:QI (match_dup 0))
(match_operand 3)))]
- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2))
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && SIBLING_CALL_P (peep2_next_insn (2))
&& !reg_mentioned_p (operands[0],
CALL_INSN_FUNCTION_USAGE (peep2_next_insn (2)))"
[(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
@@ -12863,7 +12873,7 @@
(define_insn "*call_value_pop"
[(set (match_operand 0)
- (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lmBz"))
+ (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lBwBz"))
(match_operand 2)))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 7b17773592b..c6be5f94773 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -1045,3 +1045,7 @@ Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
EnumValue
Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
+
+mindirect-branch-register
+Target Report Var(ix86_indirect_branch_thunk_register) Init(0)
+Force indirect call and jump via register.
diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
index 6fb2b4daf67..5ae443231b8 100644
--- a/gcc/config/i386/predicates.md
+++ b/gcc/config/i386/predicates.md
@@ -665,7 +665,8 @@
;; Test for a valid operand for indirect branch.
(define_predicate "indirect_branch_operand"
(ior (match_operand 0 "register_operand")
- (and (not (match_test "TARGET_X32"))
+ (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "memory_operand"))))
;; Return true if OP is a memory operands that can be used in sibcalls.
@@ -694,7 +695,8 @@
;; Return true if OP is a GOT memory operand.
(define_predicate "GOT_memory_operand"
- (match_operand 0 "memory_operand")
+ (and (match_test "!ix86_indirect_branch_thunk_register")
+ (match_operand 0 "memory_operand"))
{
op = XEXP (op, 0);
return (GET_CODE (op) == CONST
@@ -708,9 +710,11 @@
(ior (match_test "constant_call_address_operand
(op, mode == VOIDmode ? mode : Pmode)")
(match_operand 0 "call_register_no_elim_operand")
- (ior (and (not (match_test "TARGET_X32"))
+ (ior (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (match_test "TARGET_X32 && Pmode == DImode
+ && !ix86_indirect_branch_thunk_register")
(match_operand 0 "GOT_memory_operand")))))
;; Similarly, but for tail calls, in which we cannot allow memory references.
@@ -718,14 +722,17 @@
(ior (match_test "constant_call_address_operand
(op, mode == VOIDmode ? mode : Pmode)")
(match_operand 0 "register_no_elim_operand")
- (ior (and (not (match_test "TARGET_X32"))
+ (ior (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "sibcall_memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (match_test "TARGET_X32 && Pmode == DImode
+ && !ix86_indirect_branch_thunk_register")
(match_operand 0 "GOT_memory_operand")))))
;; Return true if OP is a 32-bit GOT symbol operand.
(define_predicate "GOT32_symbol_operand"
- (match_test "GET_CODE (op) == CONST
+ (match_test "!ix86_indirect_branch_thunk_register
+ && GET_CODE (op) == CONST
&& GET_CODE (XEXP (op, 0)) == UNSPEC
&& XINT (XEXP (op, 0), 1) == UNSPEC_GOT"))
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index df945989fe8..d16006e653a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1230,7 +1230,8 @@ See RS/6000 and PowerPC Options.
-mstack-protector-guard-offset=@var{offset} @gol
-mstack-protector-guard-symbol=@var{symbol} -mmitigate-rop @gol
-mgeneral-regs-only -mcall-ms2sysv-xlogues @gol
--mindirect-branch=@var{choice} -mfunction-return==@var{choice}}
+-mindirect-branch=@var{choice} -mfunction-return==@var{choice} @gol
+-mindirect-branch-register}
@emph{x86 Windows Options}
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
@@ -26861,6 +26862,10 @@ object file. You can control this behavior for a specific function by
using the function attribute @code{function_return}.
@xref{Function Attributes}.
+@item -mindirect-branch-register
+@opindex -mindirect-branch-register
+Force indirect call and jump via register.
+
@end table
These @samp{-m} switches are supported in addition to the above
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
index 527e447aea5..3f9b5f58ecc 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
index 7dbc7607e2e..19f4d5e3a1e 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
index c085b21582c..304a641db28 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
index f92968bf616..c4eabadea7d 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
index 4d19fac21d8..defe4389354 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
index 5cbdd85303e..5b202526968 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
index c59dd049883..d5aa68f52ca 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
index 61b9c80de33..09c35e73443 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
index dcd2381c514..246300e8d71 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
index 21b69728796..02223f8d0f4 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
index 0bd6aab2fd6..a80b46af934 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
index 99226dbdd1f..e85057bc098 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
index aceb4041275..50a2d72ae16 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
index 43d19bbe876..eac9b3dee22 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
index bbfaf6ba7e7..58285f6ee6c 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { ! x32 } } } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
void (*dispatch) (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
index 6c82a236c1b..d3dec5623eb 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { ! x32 } } } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
void (*dispatch) (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
index 299940de399..c2e07d59ca4 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
void bar (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
index 77ee84b938b..16b64a46dbd 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
void bar (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
index 782960375af..cf499879513 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
index 240c15be8a6..54c6e300295 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
index 6e49707875e..925c7943662 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
index e1d8891380c..0f7e39f914a 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
index 6ad05b70604..1d34cb197a7 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
index cfb0894ae49..8d591d562a5 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
index 205b9b405bf..a288e3d61b9 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
index efa0096e1e0..f7fad345ca4 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
index 775d0b8c53e..91388544a20 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
index 788271f049f..69f03e6472e 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
index ef8a2c746a7..226b776abcf 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
index 848ceefca02..b9120017c10 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
index 64608100782..fbd6f9ec457 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
index 3c2758360f5..2553c56f97f 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
new file mode 100644
index 00000000000..7d396a31953
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -mindirect-branch-register -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk\n" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk_bnd\n" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
new file mode 100644
index 00000000000..e7e616bb271
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -mindirect-branch-register -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
new file mode 100644
index 00000000000..5320e923be2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -mindirect-branch-register -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } */
+/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause|nop)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
index b5164bfc5ad..c440d68ec0d 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
index a26ac963ea5..674a6bf96e8 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
index d0106da38ee..3f37e4375de 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
index 185ad366190..f96e6847f13 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
extern void (*bar) (void);
extern int foo (void) __attribute__ ((function_return("thunk")));
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
index cce8b20b5f1..4c47b28691e 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
index 0316d301d9c..43d84743851 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
index 92298c362ec..a56a4849f76 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
extern void (*bar) (void);
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/5] x86: Add -mindirect-branch-register
2018-01-14 3:37 ` [PATCH 3/5] x86: Add -mindirect-branch-register H.J. Lu
@ 2018-01-14 10:47 ` Jan Hubicka
2018-01-14 15:13 ` H.J. Lu
0 siblings, 1 reply; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 10:47 UTC (permalink / raw)
To: H.J. Lu; +Cc: gcc-patches
> Add -mindirect-branch-register to force indirect branch via register.
> This is implemented by disabling patterns of indirect branch via memory,
> similar to TARGET_X32.
>
> -mindirect-branch= and -mfunction-return= tests are updated with
> -mno-indirect-branch-register to avoid false test failures when
> -mindirect-branch-register is added to RUNTESTFLAGS for "make check".
>
> gcc/
>
> * config/i386/constraints.md (Bs): Disallow memory operand for
> -mindirect-branch-register.
> (Bw): Likewise.
> * config/i386/predicates.md (indirect_branch_operand): Likewise.
> (GOT_memory_operand): Likewise.
> (call_insn_operand): Likewise.
> (sibcall_insn_operand): Likewise.
> (GOT32_symbol_operand): Likewise.
> * config/i386/i386.md (indirect_jump): Call convert_memory_address
> for -mindirect-branch-register.
> (tablejump): Likewise.
> (*sibcall_memory): Likewise.
> (*sibcall_value_memory): Likewise.
> Disallow peepholes of indirect call and jump via memory for
> -mindirect-branch-register.
> (*call_pop): Replace m with Bw.
> (*call_value_pop): Likewise.
> (*sibcall_pop_memory): Replace m with Bs.
> * config/i386/i386.opt (mindirect-branch-register): New option.
> * doc/invoke.texi: Document -mindirect-branch-register option.
OK.
Honza
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/5] x86: Add -mindirect-branch-register
2018-01-14 10:47 ` Jan Hubicka
@ 2018-01-14 15:13 ` H.J. Lu
0 siblings, 0 replies; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 15:13 UTC (permalink / raw)
To: Jan Hubicka, Uros Bizjak; +Cc: GCC Patches
[-- Attachment #1: Type: text/plain, Size: 1537 bytes --]
On Sun, Jan 14, 2018 at 2:46 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Add -mindirect-branch-register to force indirect branch via register.
>> This is implemented by disabling patterns of indirect branch via memory,
>> similar to TARGET_X32.
>>
>> -mindirect-branch= and -mfunction-return= tests are updated with
>> -mno-indirect-branch-register to avoid false test failures when
>> -mindirect-branch-register is added to RUNTESTFLAGS for "make check".
>>
>> gcc/
>>
>> * config/i386/constraints.md (Bs): Disallow memory operand for
>> -mindirect-branch-register.
>> (Bw): Likewise.
>> * config/i386/predicates.md (indirect_branch_operand): Likewise.
>> (GOT_memory_operand): Likewise.
>> (call_insn_operand): Likewise.
>> (sibcall_insn_operand): Likewise.
>> (GOT32_symbol_operand): Likewise.
>> * config/i386/i386.md (indirect_jump): Call convert_memory_address
>> for -mindirect-branch-register.
>> (tablejump): Likewise.
>> (*sibcall_memory): Likewise.
>> (*sibcall_value_memory): Likewise.
>> Disallow peepholes of indirect call and jump via memory for
>> -mindirect-branch-register.
>> (*call_pop): Replace m with Bw.
>> (*call_value_pop): Likewise.
>> (*sibcall_pop_memory): Replace m with Bs.
>> * config/i386/i386.opt (mindirect-branch-register): New option.
>> * doc/invoke.texi: Document -mindirect-branch-register option.
>
> OK.
> Honza
Here is the backport for GCC 7. OK for gcc-7-branch?
--
H.J.
[-- Attachment #2: 0003-x86-Add-mindirect-branch-register.patch --]
[-- Type: text/x-patch, Size: 42021 bytes --]
From 139dd2c61a11430263f91030910e2b63a73a11e7 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 6 Jan 2018 22:29:56 -0800
Subject: [PATCH 3/5] x86: Add -mindirect-branch-register
Add -mindirect-branch-register to force indirect branch via register.
This is implemented by disabling patterns of indirect branch via memory,
similar to TARGET_X32.
-mindirect-branch= and -mfunction-return= tests are updated with
-mno-indirect-branch-register to avoid false test failures when
-mindirect-branch-register is added to RUNTESTFLAGS for "make check".
gcc/
Backport from mainline
* config/i386/constraints.md (Bs): Disallow memory operand for
-mindirect-branch-register.
(Bw): Likewise.
* config/i386/predicates.md (indirect_branch_operand): Likewise.
(GOT_memory_operand): Likewise.
(call_insn_operand): Likewise.
(sibcall_insn_operand): Likewise.
(GOT32_symbol_operand): Likewise.
* config/i386/i386.md (indirect_jump): Call convert_memory_address
for -mindirect-branch-register.
(tablejump): Likewise.
(*sibcall_memory): Likewise.
(*sibcall_value_memory): Likewise.
Disallow peepholes of indirect call and jump via memory for
-mindirect-branch-register.
(*call_pop): Replace m with Bw.
(*call_value_pop): Likewise.
(*sibcall_pop_memory): Replace m with Bs.
* config/i386/i386.opt (mindirect-branch-register): New option.
* doc/invoke.texi: Document -mindirect-branch-register option.
gcc/testsuite/
Backport from mainline
* gcc.target/i386/indirect-thunk-1.c (dg-options): Add
-mno-indirect-branch-register.
* gcc.target/i386/indirect-thunk-2.c: Likewise.
* gcc.target/i386/indirect-thunk-3.c: Likewise.
* gcc.target/i386/indirect-thunk-4.c: Likewise.
* gcc.target/i386/indirect-thunk-5.c: Likewise.
* gcc.target/i386/indirect-thunk-6.c: Likewise.
* gcc.target/i386/indirect-thunk-7.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-1.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-2.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-3.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-4.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-5.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-6.c: Likewise.
* gcc.target/i386/indirect-thunk-attr-7.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-1.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-2.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-3.c: Likewise.
* gcc.target/i386/indirect-thunk-bnd-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-1.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-2.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-3.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-4.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-5.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-6.c: Likewise.
* gcc.target/i386/indirect-thunk-extern-7.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-1.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-2.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-3.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-4.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-5.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-6.c: Likewise.
* gcc.target/i386/indirect-thunk-inline-7.c: Likewise.
* gcc.target/i386/ret-thunk-10.c: Likewise.
* gcc.target/i386/ret-thunk-11.c: Likewise.
* gcc.target/i386/ret-thunk-12.c: Likewise.
* gcc.target/i386/ret-thunk-13.c: Likewise.
* gcc.target/i386/ret-thunk-14.c: Likewise.
* gcc.target/i386/ret-thunk-15.c: Likewise.
* gcc.target/i386/ret-thunk-9.c: Likewise.
* gcc.target/i386/indirect-thunk-register-1.c: New test.
* gcc.target/i386/indirect-thunk-register-2.c: Likewise.
* gcc.target/i386/indirect-thunk-register-3.c: Likewise.
---
gcc/config/i386/constraints.md | 12 +++++---
gcc/config/i386/i386.md | 34 ++++++++++++++--------
gcc/config/i386/i386.opt | 4 +++
gcc/config/i386/predicates.md | 21 ++++++++-----
gcc/doc/invoke.texi | 7 ++++-
gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 2 +-
gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-attr-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-bnd-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-extern-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-1.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-2.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-3.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-4.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-5.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-6.c | 2 +-
.../gcc.target/i386/indirect-thunk-inline-7.c | 2 +-
.../gcc.target/i386/indirect-thunk-register-1.c | 22 ++++++++++++++
.../gcc.target/i386/indirect-thunk-register-2.c | 20 +++++++++++++
.../gcc.target/i386/indirect-thunk-register-3.c | 19 ++++++++++++
gcc/testsuite/gcc.target/i386/ret-thunk-10.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-11.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-12.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-13.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-14.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-15.c | 2 +-
gcc/testsuite/gcc.target/i386/ret-thunk-9.c | 2 +-
47 files changed, 154 insertions(+), 63 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
diff --git a/gcc/config/i386/constraints.md b/gcc/config/i386/constraints.md
index 38d604fdace..697caf704dd 100644
--- a/gcc/config/i386/constraints.md
+++ b/gcc/config/i386/constraints.md
@@ -198,16 +198,20 @@
(define_constraint "Bs"
"@internal Sibcall memory operand."
- (ior (and (not (match_test "TARGET_X32"))
+ (ior (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "sibcall_memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (match_test "TARGET_X32 && Pmode == DImode
+ && !ix86_indirect_branch_thunk_register")
(match_operand 0 "GOT_memory_operand"))))
(define_constraint "Bw"
"@internal Call memory operand."
- (ior (and (not (match_test "TARGET_X32"))
+ (ior (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (match_test "TARGET_X32 && Pmode == DImode
+ && !ix86_indirect_branch_thunk_register")
(match_operand 0 "GOT_memory_operand"))))
(define_constraint "Bz"
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d112bdb8552..96941e8c5c4 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -11625,7 +11625,7 @@
[(set (pc) (match_operand 0 "indirect_branch_operand"))]
""
{
- if (TARGET_X32)
+ if (TARGET_X32 || ix86_indirect_branch_thunk_register)
operands[0] = convert_memory_address (word_mode, operands[0]);
cfun->machine->has_local_indirect_jump = true;
})
@@ -11679,7 +11679,7 @@
OPTAB_DIRECT);
}
- if (TARGET_X32)
+ if (TARGET_X32 || ix86_indirect_branch_thunk_register)
operands[0] = convert_memory_address (word_mode, operands[0]);
cfun->machine->has_local_indirect_jump = true;
})
@@ -11871,7 +11871,7 @@
[(call (mem:QI (match_operand:W 0 "memory_operand" "m"))
(match_operand 1))
(unspec [(const_int 0)] UNSPEC_PEEPSIB)]
- "!TARGET_X32"
+ "!TARGET_X32 && !ix86_indirect_branch_thunk_register"
"* return ix86_output_call_insn (insn, operands[0]);"
[(set_attr "type" "call")])
@@ -11880,7 +11880,9 @@
(match_operand:W 1 "memory_operand"))
(call (mem:QI (match_dup 0))
(match_operand 3))]
- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1))
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && SIBLING_CALL_P (peep2_next_insn (1))
&& !reg_mentioned_p (operands[0],
CALL_INSN_FUNCTION_USAGE (peep2_next_insn (1)))"
[(parallel [(call (mem:QI (match_dup 1))
@@ -11893,7 +11895,9 @@
(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
(call (mem:QI (match_dup 0))
(match_operand 3))]
- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2))
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && SIBLING_CALL_P (peep2_next_insn (2))
&& !reg_mentioned_p (operands[0],
CALL_INSN_FUNCTION_USAGE (peep2_next_insn (2)))"
[(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
@@ -11915,7 +11919,7 @@
})
(define_insn "*call_pop"
- [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lmBz"))
+ [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lBwBz"))
(match_operand 1))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
@@ -11935,7 +11939,7 @@
[(set_attr "type" "call")])
(define_insn "*sibcall_pop_memory"
- [(call (mem:QI (match_operand:SI 0 "memory_operand" "m"))
+ [(call (mem:QI (match_operand:SI 0 "memory_operand" "Bs"))
(match_operand 1))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
@@ -11989,7 +11993,9 @@
[(set (match_operand:W 0 "register_operand")
(match_operand:W 1 "memory_operand"))
(set (pc) (match_dup 0))]
- "!TARGET_X32 && peep2_reg_dead_p (2, operands[0])"
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && peep2_reg_dead_p (2, operands[0])"
[(set (pc) (match_dup 1))])
;; Call subroutine, returning value in operand 0
@@ -12070,7 +12076,7 @@
(call (mem:QI (match_operand:W 1 "memory_operand" "m"))
(match_operand 2)))
(unspec [(const_int 0)] UNSPEC_PEEPSIB)]
- "!TARGET_X32"
+ "!TARGET_X32 && !ix86_indirect_branch_thunk_register"
"* return ix86_output_call_insn (insn, operands[1]);"
[(set_attr "type" "callv")])
@@ -12080,7 +12086,9 @@
(set (match_operand 2)
(call (mem:QI (match_dup 0))
(match_operand 3)))]
- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1))
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && SIBLING_CALL_P (peep2_next_insn (1))
&& !reg_mentioned_p (operands[0],
CALL_INSN_FUNCTION_USAGE (peep2_next_insn (1)))"
[(parallel [(set (match_dup 2)
@@ -12095,7 +12103,9 @@
(set (match_operand 2)
(call (mem:QI (match_dup 0))
(match_operand 3)))]
- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2))
+ "!TARGET_X32
+ && !ix86_indirect_branch_thunk_register
+ && SIBLING_CALL_P (peep2_next_insn (2))
&& !reg_mentioned_p (operands[0],
CALL_INSN_FUNCTION_USAGE (peep2_next_insn (2)))"
[(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
@@ -12120,7 +12130,7 @@
(define_insn "*call_value_pop"
[(set (match_operand 0)
- (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lmBz"))
+ (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lBwBz"))
(match_operand 2)))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index b07388d95a9..852033cbb67 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -951,3 +951,7 @@ Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
EnumValue
Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
+
+mindirect-branch-register
+Target Report Var(ix86_indirect_branch_thunk_register) Init(0)
+Force indirect call and jump via register.
diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
index 2fc2c60f6ac..a88b1d860ca 100644
--- a/gcc/config/i386/predicates.md
+++ b/gcc/config/i386/predicates.md
@@ -635,7 +635,8 @@
;; Test for a valid operand for indirect branch.
(define_predicate "indirect_branch_operand"
(ior (match_operand 0 "register_operand")
- (and (not (match_test "TARGET_X32"))
+ (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "memory_operand"))))
;; Return true if OP is a memory operands that can be used in sibcalls.
@@ -664,7 +665,8 @@
;; Return true if OP is a GOT memory operand.
(define_predicate "GOT_memory_operand"
- (match_operand 0 "memory_operand")
+ (and (match_test "!ix86_indirect_branch_thunk_register")
+ (match_operand 0 "memory_operand"))
{
op = XEXP (op, 0);
return (GET_CODE (op) == CONST
@@ -678,9 +680,11 @@
(ior (match_test "constant_call_address_operand
(op, mode == VOIDmode ? mode : Pmode)")
(match_operand 0 "call_register_no_elim_operand")
- (ior (and (not (match_test "TARGET_X32"))
+ (ior (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (match_test "TARGET_X32 && Pmode == DImode
+ && !ix86_indirect_branch_thunk_register")
(match_operand 0 "GOT_memory_operand")))))
;; Similarly, but for tail calls, in which we cannot allow memory references.
@@ -688,14 +692,17 @@
(ior (match_test "constant_call_address_operand
(op, mode == VOIDmode ? mode : Pmode)")
(match_operand 0 "register_no_elim_operand")
- (ior (and (not (match_test "TARGET_X32"))
+ (ior (and (not (match_test "TARGET_X32
+ || ix86_indirect_branch_thunk_register"))
(match_operand 0 "sibcall_memory_operand"))
- (and (match_test "TARGET_X32 && Pmode == DImode")
+ (and (match_test "TARGET_X32 && Pmode == DImode
+ && !ix86_indirect_branch_thunk_register")
(match_operand 0 "GOT_memory_operand")))))
;; Return true if OP is a 32-bit GOT symbol operand.
(define_predicate "GOT32_symbol_operand"
- (match_test "GET_CODE (op) == CONST
+ (match_test "!ix86_indirect_branch_thunk_register
+ && GET_CODE (op) == CONST
&& GET_CODE (XEXP (op, 0)) == UNSPEC
&& XINT (XEXP (op, 0), 1) == UNSPEC_GOT"))
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f3eb54b1668..1e572b1f9a2 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1211,7 +1211,8 @@ See RS/6000 and PowerPC Options.
-mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol
-malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol
-mmitigate-rop -mgeneral-regs-only @gol
--mindirect-branch=@var{choice} -mfunction-return==@var{choice}}
+-mindirect-branch=@var{choice} -mfunction-return==@var{choice} @gol
+-mindirect-branch-register}
@emph{x86 Windows Options}
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
@@ -25709,6 +25710,10 @@ object file. You can control this behavior for a specific function by
using the function attribute @code{function_return}.
@xref{Function Attributes}.
+@item -mindirect-branch-register
+@opindex -mindirect-branch-register
+Force indirect call and jump via register.
+
@end table
These @samp{-m} switches are supported in addition to the above
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
index f076155c91a..9eb9b273ade 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
index d7984f592fe..c63795e4127 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
index 3257d0a2e16..82973cda771 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
index 7cab2df6474..a5f3d1cbed8 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
index b4836c38d6c..fcaa18d10b7 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
index 1f06bd1af74..e4649283d10 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
index 0b3fef86a20..ebfb8aab937 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
index 5f6cfc17b56..a08022db8e4 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
index b256160ec80..b257c695ad1 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
index 567c95051d6..dfb1370d23d 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
index 3b662af7d5d..a6e3f6f9f2b 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
index 98785a38248..4bb1c5f9220 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
index a498a39e404..4e33a638862 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
index 66f295d1eb6..427ba3ddbb4 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
index aacb814d737..dc7143414fb 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { ! x32 } } } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
void (*dispatch) (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
index 7b44dda23df..737c60946f6 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { ! x32 } } } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
void (*dispatch) (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
index 70b4fb36eea..d34485a0010 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
void bar (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
index 3baf03ee77c..0e19830de4d 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
void bar (char *);
char buf[10];
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
index 637fc3d3f4e..5c20a35ecec 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
index ff9efe03fe6..b2fb6e1bcd2 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
index 2686a5f2db4..9c84547cd7c 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
index f07f6b214ad..457849564bb 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
index 21740ac5b7f..5c07e02df6a 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
index a77c1f470b8..3eb440693a0 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
index e64910fd4aa..d4747ea0764 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
index 365cf2ee226..536abfa74e4 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
index 72646a4960b..bd2b6246aa1 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
index f48945e3dfc..9885eebbcff 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
index 4b1d558fc4e..7b3983949d2 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
typedef void (*dispatch_t)(long offset);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
index 0f687c3b027..c6d77e10352 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
index b27c6fc96a2..6454827b780 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile { target *-*-linux* } } */
-/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
extern void bar (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
index 2c496492eaa..cc592f89aba 100644
--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
void func0 (void);
void func1 (void);
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
new file mode 100644
index 00000000000..7d396a31953
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -mindirect-branch-register -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk\n" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk_bnd\n" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
new file mode 100644
index 00000000000..e7e616bb271
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -mindirect-branch-register -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
+/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
new file mode 100644
index 00000000000..5320e923be2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -mindirect-branch-register -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+ dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } */
+/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */
+/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */
+/* { dg-final { scan-assembler-not {\t(pause|pause|nop)} } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
index 1630e2fa2b5..b4f9d48065d 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
index 876159cf783..0312577a043 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
index 01b0a02f80b..fa3181303c9 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
index e028c2b6a99..7a08e71c76b 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */
extern void (*bar) (void);
extern int foo (void) __attribute__ ((function_return("thunk")));
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
index c14ee3ae4c0..dacf0c769fc 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
index 2f21e138ec2..cf06a5f35c7 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */
extern void (*bar) (void);
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
index f6ccad98da7..6da5ab97081 100644
--- a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */
extern void (*bar) (void);
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 4/5] x86: Add 'V' register operand modifier
2018-01-14 3:37 [PATCH 0/5] x86: CVE-2017-5715, aka Spectre H.J. Lu
` (3 preceding siblings ...)
2018-01-14 3:37 ` [PATCH 3/5] x86: Add -mindirect-branch-register H.J. Lu
@ 2018-01-14 3:40 ` H.J. Lu
2018-01-14 10:47 ` Jan Hubicka
2018-01-14 8:48 ` [PATCH 0/5] x86: CVE-2017-5715, aka Spectre Kumar, Venkataramanan
5 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 3:40 UTC (permalink / raw)
To: gcc-patches
Add 'V', a special modifier which prints the name of the full integer
register without '%'. For
extern void (*func_p) (void);
void
foo (void)
{
asm ("call __x86_indirect_thunk_%V0" : : "a" (func_p));
}
it generates:
foo:
movq func_p(%rip), %rax
call __x86_indirect_thunk_rax
ret
gcc/
* config/i386/i386.c (print_reg): Print the name of the full
integer register without '%'.
(ix86_print_operand): Handle 'V'.
* doc/extend.texi: Document 'V' modifier.
gcc/testsuite/
* gcc.target/i386/indirect-thunk-register-4.c: New test.
---
gcc/config/i386/i386.c | 13 ++++++++++++-
gcc/doc/extend.texi | 3 +++
gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c | 13 +++++++++++++
3 files changed, 28 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 836cc50cf67..9807de0c117 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -17650,6 +17650,7 @@ put_condition_code (enum rtx_code code, machine_mode mode, bool reverse,
If CODE is 'h', pretend the reg is the 'high' byte register.
If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op.
If CODE is 'd', duplicate the operand for AVX instruction.
+ If CODE is 'V', print naked full integer register name without %.
*/
void
@@ -17660,7 +17661,7 @@ print_reg (rtx x, int code, FILE *file)
unsigned int regno;
bool duplicated;
- if (ASSEMBLER_DIALECT == ASM_ATT)
+ if (ASSEMBLER_DIALECT == ASM_ATT && code != 'V')
putc ('%', file);
if (x == pc_rtx)
@@ -17712,6 +17713,14 @@ print_reg (rtx x, int code, FILE *file)
return;
}
+ if (code == 'V')
+ {
+ if (GENERAL_REGNO_P (regno))
+ msize = GET_MODE_SIZE (word_mode);
+ else
+ error ("'V' modifier on non-integer register");
+ }
+
duplicated = code == 'd' && TARGET_AVX;
switch (msize)
@@ -17831,6 +17840,7 @@ print_reg (rtx x, int code, FILE *file)
& -- print some in-use local-dynamic symbol name.
H -- print a memory address offset by 8; used for sse high-parts
Y -- print condition for XOP pcom* instruction.
+ V -- print naked full integer register name without %.
+ -- print a branch hint as 'cs' or 'ds' prefix
; -- print a semicolon (after prefixes due to bug in older gas).
~ -- print "i" if TARGET_AVX2, "f" otherwise.
@@ -18054,6 +18064,7 @@ ix86_print_operand (FILE *file, rtx x, int code)
case 'X':
case 'P':
case 'p':
+ case 'V':
break;
case 's':
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f120b2a1429..dce808f1eab 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -9292,6 +9292,9 @@ The table below shows the list of supported modifiers and their effects.
@tab @code{2}
@end multitable
+@code{V} is a special modifier which prints the name of the full integer
+register without @code{%}.
+
@anchor{x86floatingpointasmoperands}
@subsubsection x86 Floating-Point @code{asm} Operands
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
new file mode 100644
index 00000000000..f0cd9b75be8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=keep -fno-pic" } */
+
+extern void (*func_p) (void);
+
+void
+foo (void)
+{
+ asm("call __x86_indirect_thunk_%V0" : : "a" (func_p));
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_eax" { target ia32 } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_rax" { target { ! ia32 } } } } */
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 4/5] x86: Add 'V' register operand modifier
2018-01-14 3:40 ` [PATCH 4/5] x86: Add 'V' register operand modifier H.J. Lu
@ 2018-01-14 10:47 ` Jan Hubicka
2018-01-14 15:14 ` H.J. Lu
0 siblings, 1 reply; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 10:47 UTC (permalink / raw)
To: H.J. Lu; +Cc: gcc-patches
> Add 'V', a special modifier which prints the name of the full integer
> register without '%'. For
>
> extern void (*func_p) (void);
>
> void
> foo (void)
> {
> asm ("call __x86_indirect_thunk_%V0" : : "a" (func_p));
> }
>
> it generates:
>
> foo:
> movq func_p(%rip), %rax
> call __x86_indirect_thunk_rax
> ret
>
> gcc/
>
> * config/i386/i386.c (print_reg): Print the name of the full
> integer register without '%'.
> (ix86_print_operand): Handle 'V'.
> * doc/extend.texi: Document 'V' modifier.
>
> gcc/testsuite/
>
> * gcc.target/i386/indirect-thunk-register-4.c: New test.
OK.
Honza
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 4/5] x86: Add 'V' register operand modifier
2018-01-14 10:47 ` Jan Hubicka
@ 2018-01-14 15:14 ` H.J. Lu
0 siblings, 0 replies; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 15:14 UTC (permalink / raw)
To: Jan Hubicka, Uros Bizjak; +Cc: GCC Patches
[-- Attachment #1: Type: text/plain, Size: 815 bytes --]
On Sun, Jan 14, 2018 at 2:47 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Add 'V', a special modifier which prints the name of the full integer
>> register without '%'. For
>>
>> extern void (*func_p) (void);
>>
>> void
>> foo (void)
>> {
>> asm ("call __x86_indirect_thunk_%V0" : : "a" (func_p));
>> }
>>
>> it generates:
>>
>> foo:
>> movq func_p(%rip), %rax
>> call __x86_indirect_thunk_rax
>> ret
>>
>> gcc/
>>
>> * config/i386/i386.c (print_reg): Print the name of the full
>> integer register without '%'.
>> (ix86_print_operand): Handle 'V'.
>> * doc/extend.texi: Document 'V' modifier.
>>
>> gcc/testsuite/
>>
>> * gcc.target/i386/indirect-thunk-register-4.c: New test.
>
> OK.
> Honza
Here is the backport for GCC 7. OK for gcc-7-branch?
--
H.J.
[-- Attachment #2: 0004-x86-Add-V-register-operand-modifier.patch --]
[-- Type: text/x-patch, Size: 4037 bytes --]
From 5e977dfedb93e764dc480c0e0674500590ef5604 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 6 Jan 2018 22:29:56 -0800
Subject: [PATCH 4/5] x86: Add 'V' register operand modifier
Add 'V', a special modifier which prints the name of the full integer
register without '%'. For
extern void (*func_p) (void);
void
foo (void)
{
asm ("call __x86_indirect_thunk_%V0" : : "a" (func_p));
}
it generates:
foo:
movq func_p(%rip), %rax
call __x86_indirect_thunk_rax
ret
gcc/
Backport from mainline
* config/i386/i386.c (print_reg): Print the name of the full
integer register without '%'.
(ix86_print_operand): Handle 'V'.
* doc/extend.texi: Document 'V' modifier.
gcc/testsuite/
Backport from mainline
* gcc.target/i386/indirect-thunk-register-4.c: New test.
---
gcc/config/i386/i386.c | 13 ++++++++++++-
gcc/doc/extend.texi | 3 +++
gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c | 13 +++++++++++++
3 files changed, 28 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 4bfe2fa8c1d..e32de13688a 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -17925,6 +17925,7 @@ put_condition_code (enum rtx_code code, machine_mode mode, bool reverse,
If CODE is 'h', pretend the reg is the 'high' byte register.
If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op.
If CODE is 'd', duplicate the operand for AVX instruction.
+ If CODE is 'V', print naked full integer register name without %.
*/
void
@@ -17935,7 +17936,7 @@ print_reg (rtx x, int code, FILE *file)
unsigned int regno;
bool duplicated;
- if (ASSEMBLER_DIALECT == ASM_ATT)
+ if (ASSEMBLER_DIALECT == ASM_ATT && code != 'V')
putc ('%', file);
if (x == pc_rtx)
@@ -17983,6 +17984,14 @@ print_reg (rtx x, int code, FILE *file)
return;
}
+ if (code == 'V')
+ {
+ if (GENERAL_REGNO_P (regno))
+ msize = GET_MODE_SIZE (word_mode);
+ else
+ error ("'V' modifier on non-integer register");
+ }
+
duplicated = code == 'd' && TARGET_AVX;
switch (msize)
@@ -18102,6 +18111,7 @@ print_reg (rtx x, int code, FILE *file)
& -- print some in-use local-dynamic symbol name.
H -- print a memory address offset by 8; used for sse high-parts
Y -- print condition for XOP pcom* instruction.
+ V -- print naked full integer register name without %.
+ -- print a branch hint as 'cs' or 'ds' prefix
; -- print a semicolon (after prefixes due to bug in older gas).
~ -- print "i" if TARGET_AVX2, "f" otherwise.
@@ -18326,6 +18336,7 @@ ix86_print_operand (FILE *file, rtx x, int code)
case 'X':
case 'P':
case 'p':
+ case 'V':
break;
case 's':
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 46e0a3623a6..9db9e0e27e9 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8778,6 +8778,9 @@ The table below shows the list of supported modifiers and their effects.
@tab @code{2}
@end multitable
+@code{V} is a special modifier which prints the name of the full integer
+register without @code{%}.
+
@anchor{x86floatingpointasmoperands}
@subsubsection x86 Floating-Point @code{asm} Operands
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
new file mode 100644
index 00000000000..f0cd9b75be8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=keep -fno-pic" } */
+
+extern void (*func_p) (void);
+
+void
+foo (void)
+{
+ asm("call __x86_indirect_thunk_%V0" : : "a" (func_p));
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_eax" { target ia32 } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_rax" { target { ! ia32 } } } } */
--
2.14.3
^ permalink raw reply [flat|nested] 120+ messages in thread
* RE: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
2018-01-14 3:37 [PATCH 0/5] x86: CVE-2017-5715, aka Spectre H.J. Lu
` (4 preceding siblings ...)
2018-01-14 3:40 ` [PATCH 4/5] x86: Add 'V' register operand modifier H.J. Lu
@ 2018-01-14 8:48 ` Kumar, Venkataramanan
2018-01-14 10:45 ` Jan Hubicka
5 siblings, 1 reply; 120+ messages in thread
From: Kumar, Venkataramanan @ 2018-01-14 8:48 UTC (permalink / raw)
To: H.J. Lu, gcc-patches
Cc: Dharmakan, Rohit arul raj, Nagarajan, Muthu kumar raj,
Uros Bizjak (ubizjak@gmail.com), Jan Hubicka (hubicka@ucw.cz)
Hi HJ,
> -----Original Message-----
> From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
> owner@gcc.gnu.org] On Behalf Of H.J. Lu
> Sent: Sunday, January 14, 2018 9:07 AM
> To: gcc-patches@gcc.gnu.org
> Subject: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
>
> This set of patches for GCC 8 mitigates variant #2 of the speculative
> execution vulnerabilities on x86 processors identified by CVE-2017-5715, aka
> Spectre. They convert indirect branches and function returns to call and
> return thunks to avoid speculative execution via indirect call, jmp and ret.
>
> H.J. Lu (5):
> x86: Add -mindirect-branch=
> x86: Add -mfunction-return=
> x86: Add -mindirect-branch-register
> x86: Add 'V' register operand modifier
> x86: Disallow -mindirect-branch=/-mfunction-return= with
> -mcmodel=large
Current set of patches don't seem to have any option to generate "lfence" as the loop filler in "retpoline", which is required by AMD.
Can you please clarify the plan. We would like to get this checked-in GCC 8.
>
> gcc/config/i386/constraints.md | 12 +-
> gcc/config/i386/i386-opts.h | 13 +
> gcc/config/i386/i386-protos.h | 2 +
> gcc/config/i386/i386.c | 822 ++++++++++++++++++++-
> gcc/config/i386/i386.h | 10 +
> gcc/config/i386/i386.md | 69 +-
> gcc/config/i386/i386.opt | 28 +
> gcc/config/i386/predicates.md | 21 +-
> gcc/doc/extend.texi | 22 +
> gcc/doc/invoke.texi | 41 +-
> gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 19 +
> gcc/testsuite/gcc.target/i386/indirect-thunk-10.c | 7 +
> gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 19 +
> gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 20 +
> gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 20 +
> gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 16 +
> gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 17 +
> gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 43 ++
> gcc/testsuite/gcc.target/i386/indirect-thunk-8.c | 7 +
> gcc/testsuite/gcc.target/i386/indirect-thunk-9.c | 7 +
> .../gcc.target/i386/indirect-thunk-attr-1.c | 22 +
> .../gcc.target/i386/indirect-thunk-attr-10.c | 9 +
> .../gcc.target/i386/indirect-thunk-attr-11.c | 9 +
> .../gcc.target/i386/indirect-thunk-attr-2.c | 20 +
> .../gcc.target/i386/indirect-thunk-attr-3.c | 21 +
> .../gcc.target/i386/indirect-thunk-attr-4.c | 20 +
> .../gcc.target/i386/indirect-thunk-attr-5.c | 22 +
> .../gcc.target/i386/indirect-thunk-attr-6.c | 21 +
> .../gcc.target/i386/indirect-thunk-attr-7.c | 44 ++
> .../gcc.target/i386/indirect-thunk-attr-8.c | 41 +
> .../gcc.target/i386/indirect-thunk-attr-9.c | 9 +
> .../gcc.target/i386/indirect-thunk-bnd-1.c | 19 +
> .../gcc.target/i386/indirect-thunk-bnd-2.c | 20 +
> .../gcc.target/i386/indirect-thunk-bnd-3.c | 18 +
> .../gcc.target/i386/indirect-thunk-bnd-4.c | 19 +
> .../gcc.target/i386/indirect-thunk-extern-1.c | 19 +
> .../gcc.target/i386/indirect-thunk-extern-2.c | 19 +
> .../gcc.target/i386/indirect-thunk-extern-3.c | 20 +
> .../gcc.target/i386/indirect-thunk-extern-4.c | 20 +
> .../gcc.target/i386/indirect-thunk-extern-5.c | 16 +
> .../gcc.target/i386/indirect-thunk-extern-6.c | 17 +
> .../gcc.target/i386/indirect-thunk-extern-7.c | 43 ++
> .../gcc.target/i386/indirect-thunk-inline-1.c | 18 +
> .../gcc.target/i386/indirect-thunk-inline-2.c | 18 +
> .../gcc.target/i386/indirect-thunk-inline-3.c | 19 +
> .../gcc.target/i386/indirect-thunk-inline-4.c | 19 +
> .../gcc.target/i386/indirect-thunk-inline-5.c | 15 +
> .../gcc.target/i386/indirect-thunk-inline-6.c | 16 +
> .../gcc.target/i386/indirect-thunk-inline-7.c | 42 ++
> .../gcc.target/i386/indirect-thunk-register-1.c | 22 +
> .../gcc.target/i386/indirect-thunk-register-2.c | 20 +
> .../gcc.target/i386/indirect-thunk-register-3.c | 19 +
> .../gcc.target/i386/indirect-thunk-register-4.c | 13 +
> gcc/testsuite/gcc.target/i386/ret-thunk-1.c | 12 +
> gcc/testsuite/gcc.target/i386/ret-thunk-10.c | 22 +
> gcc/testsuite/gcc.target/i386/ret-thunk-11.c | 22 +
> gcc/testsuite/gcc.target/i386/ret-thunk-12.c | 21 +
> gcc/testsuite/gcc.target/i386/ret-thunk-13.c | 21 +
> gcc/testsuite/gcc.target/i386/ret-thunk-14.c | 21 +
> gcc/testsuite/gcc.target/i386/ret-thunk-15.c | 21 +
> gcc/testsuite/gcc.target/i386/ret-thunk-16.c | 18 +
> gcc/testsuite/gcc.target/i386/ret-thunk-17.c | 7 +
> gcc/testsuite/gcc.target/i386/ret-thunk-18.c | 8 +
> gcc/testsuite/gcc.target/i386/ret-thunk-19.c | 8 +
> gcc/testsuite/gcc.target/i386/ret-thunk-2.c | 12 +
> gcc/testsuite/gcc.target/i386/ret-thunk-20.c | 9 +
> gcc/testsuite/gcc.target/i386/ret-thunk-21.c | 9 +
> gcc/testsuite/gcc.target/i386/ret-thunk-3.c | 12 +
> gcc/testsuite/gcc.target/i386/ret-thunk-4.c | 12 +
> gcc/testsuite/gcc.target/i386/ret-thunk-5.c | 14 +
> gcc/testsuite/gcc.target/i386/ret-thunk-6.c | 13 +
> gcc/testsuite/gcc.target/i386/ret-thunk-7.c | 13 +
> gcc/testsuite/gcc.target/i386/ret-thunk-8.c | 14 +
> gcc/testsuite/gcc.target/i386/ret-thunk-9.c | 23 +
> 74 files changed, 2166 insertions(+), 50 deletions(-) create mode 100644
> gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-10.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-8.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-9.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-
> 1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-
> 2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-
> 3.c
> create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-
> 4.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-10.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-11.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-12.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-13.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-14.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-15.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-16.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-17.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-18.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-19.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-20.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-21.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-3.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-4.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-5.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-6.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-7.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-8.c
> create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-9.c
>
> --
> 2.14.3
Regards,
Venkat.
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
2018-01-14 8:48 ` [PATCH 0/5] x86: CVE-2017-5715, aka Spectre Kumar, Venkataramanan
@ 2018-01-14 10:45 ` Jan Hubicka
2018-01-14 12:33 ` Uros Bizjak
2018-01-14 14:22 ` Jan Hubicka
0 siblings, 2 replies; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 10:45 UTC (permalink / raw)
To: Kumar, Venkataramanan
Cc: H.J. Lu, gcc-patches, Dharmakan, Rohit arul raj, Nagarajan,
Muthu kumar raj, Uros Bizjak (ubizjak@gmail.com)
> Hi HJ,
>
> > -----Original Message-----
> > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
> > owner@gcc.gnu.org] On Behalf Of H.J. Lu
> > Sent: Sunday, January 14, 2018 9:07 AM
> > To: gcc-patches@gcc.gnu.org
> > Subject: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
> >
> > This set of patches for GCC 8 mitigates variant #2 of the speculative
> > execution vulnerabilities on x86 processors identified by CVE-2017-5715, aka
> > Spectre. They convert indirect branches and function returns to call and
> > return thunks to avoid speculative execution via indirect call, jmp and ret.
> >
> > H.J. Lu (5):
> > x86: Add -mindirect-branch=
> > x86: Add -mfunction-return=
> > x86: Add -mindirect-branch-register
> > x86: Add 'V' register operand modifier
> > x86: Disallow -mindirect-branch=/-mfunction-return= with
> > -mcmodel=large
>
> Current set of patches don't seem to have any option to generate "lfence" as the loop filler in "retpoline",  which is required by AMD.
> Can you please clarify the plan. We would like to get this checked-in GCC 8.Â
Since thunks are output as strings, it is easy to add the option
on the top of patch #1 of the series. I do not fully understand
the reason for choosing pause over lfence for Intel, but if we need
to do both, we need to have command line option (and possibly attribute).
What would be reasonable name for it?
Honza
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
2018-01-14 10:45 ` Jan Hubicka
@ 2018-01-14 12:33 ` Uros Bizjak
2018-01-14 12:52 ` Jan Hubicka
2018-01-14 14:22 ` Jan Hubicka
1 sibling, 1 reply; 120+ messages in thread
From: Uros Bizjak @ 2018-01-14 12:33 UTC (permalink / raw)
To: Jan Hubicka
Cc: Kumar, Venkataramanan, H.J. Lu, gcc-patches, Dharmakan,
Rohit arul raj, Nagarajan, Muthu kumar raj
On Sun, Jan 14, 2018 at 11:40 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Hi HJ,
>>
>> > -----Original Message-----
>> > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
>> > owner@gcc.gnu.org] On Behalf Of H.J. Lu
>> > Sent: Sunday, January 14, 2018 9:07 AM
>> > To: gcc-patches@gcc.gnu.org
>> > Subject: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
>> >
>> > This set of patches for GCC 8 mitigates variant #2 of the speculative
>> > execution vulnerabilities on x86 processors identified by CVE-2017-5715, aka
>> > Spectre. They convert indirect branches and function returns to call and
>> > return thunks to avoid speculative execution via indirect call, jmp and ret.
>> >
>> > H.J. Lu (5):
>> > x86: Add -mindirect-branch=
>> > x86: Add -mfunction-return=
>> > x86: Add -mindirect-branch-register
>> > x86: Add 'V' register operand modifier
>> > x86: Disallow -mindirect-branch=/-mfunction-return= with
>> > -mcmodel=large
>>
>> Current set of patches don't seem to have any option to generate "lfence" as the loop filler in "retpoline", which is required by AMD.
>> Can you please clarify the plan. We would like to get this checked-in GCC 8.
>
> Since thunks are output as strings, it is easy to add the option
> on the top of patch #1 of the series. I do not fully understand
> the reason for choosing pause over lfence for Intel, but if we need
> to do both, we need to have command line option (and possibly attribute).
> What would be reasonable name for it?
Looking at the kernel patch [1], the loop filler should be
"pause;lfence" sequence, and should be universally accepted for Intel
and AMD targets.
[1] https://www.spinics.net/lists/kernel/msg2697507.html
Uros.
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
2018-01-14 12:33 ` Uros Bizjak
@ 2018-01-14 12:52 ` Jan Hubicka
2018-01-14 12:55 ` H.J. Lu
0 siblings, 1 reply; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 12:52 UTC (permalink / raw)
To: Uros Bizjak
Cc: Jan Hubicka, Kumar, Venkataramanan, H.J. Lu, gcc-patches,
Dharmakan, Rohit arul raj, Nagarajan, Muthu kumar raj
> On Sun, Jan 14, 2018 at 11:40 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
> >> Hi HJ,
> >>
> >> > -----Original Message-----
> >> > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
> >> > owner@gcc.gnu.org] On Behalf Of H.J. Lu
> >> > Sent: Sunday, January 14, 2018 9:07 AM
> >> > To: gcc-patches@gcc.gnu.org
> >> > Subject: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
> >> >
> >> > This set of patches for GCC 8 mitigates variant #2 of the speculative
> >> > execution vulnerabilities on x86 processors identified by CVE-2017-5715, aka
> >> > Spectre. They convert indirect branches and function returns to call and
> >> > return thunks to avoid speculative execution via indirect call, jmp and ret.
> >> >
> >> > H.J. Lu (5):
> >> > x86: Add -mindirect-branch=
> >> > x86: Add -mfunction-return=
> >> > x86: Add -mindirect-branch-register
> >> > x86: Add 'V' register operand modifier
> >> > x86: Disallow -mindirect-branch=/-mfunction-return= with
> >> > -mcmodel=large
> >>
> >> Current set of patches don't seem to have any option to generate "lfence" as the loop filler in "retpoline", which is required by AMD.
> >> Can you please clarify the plan. We would like to get this checked-in GCC 8.
> >
> > Since thunks are output as strings, it is easy to add the option
> > on the top of patch #1 of the series. I do not fully understand
> > the reason for choosing pause over lfence for Intel, but if we need
> > to do both, we need to have command line option (and possibly attribute).
> > What would be reasonable name for it?
>
> Looking at the kernel patch [1], the loop filler should be
> "pause;lfence" sequence, and should be universally accepted for Intel
> and AMD targets.
>
> [1] https://www.spinics.net/lists/kernel/msg2697507.html
Yep, I would say we should go with pause;lfence now and see if we want to add argument
eventually.
HJ, does it sound OK?
Honza
>
> Uros.
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
2018-01-14 12:52 ` Jan Hubicka
@ 2018-01-14 12:55 ` H.J. Lu
0 siblings, 0 replies; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 12:55 UTC (permalink / raw)
To: Jan Hubicka
Cc: Uros Bizjak, Kumar, Venkataramanan, gcc-patches, Dharmakan,
Rohit arul raj, Nagarajan, Muthu kumar raj
On Sun, Jan 14, 2018 at 4:42 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> On Sun, Jan 14, 2018 at 11:40 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> >> Hi HJ,
>> >>
>> >> > -----Original Message-----
>> >> > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
>> >> > owner@gcc.gnu.org] On Behalf Of H.J. Lu
>> >> > Sent: Sunday, January 14, 2018 9:07 AM
>> >> > To: gcc-patches@gcc.gnu.org
>> >> > Subject: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
>> >> >
>> >> > This set of patches for GCC 8 mitigates variant #2 of the speculative
>> >> > execution vulnerabilities on x86 processors identified by CVE-2017-5715, aka
>> >> > Spectre. They convert indirect branches and function returns to call and
>> >> > return thunks to avoid speculative execution via indirect call, jmp and ret.
>> >> >
>> >> > H.J. Lu (5):
>> >> > x86: Add -mindirect-branch=
>> >> > x86: Add -mfunction-return=
>> >> > x86: Add -mindirect-branch-register
>> >> > x86: Add 'V' register operand modifier
>> >> > x86: Disallow -mindirect-branch=/-mfunction-return= with
>> >> > -mcmodel=large
>> >>
>> >> Current set of patches don't seem to have any option to generate "lfence" as the loop filler in "retpoline", which is required by AMD.
>> >> Can you please clarify the plan. We would like to get this checked-in GCC 8.
>> >
>> > Since thunks are output as strings, it is easy to add the option
>> > on the top of patch #1 of the series. I do not fully understand
>> > the reason for choosing pause over lfence for Intel, but if we need
>> > to do both, we need to have command line option (and possibly attribute).
>> > What would be reasonable name for it?
>>
>> Looking at the kernel patch [1], the loop filler should be
>> "pause;lfence" sequence, and should be universally accepted for Intel
>> and AMD targets.
>>
>> [1] https://www.spinics.net/lists/kernel/msg2697507.html
>
> Yep, I would say we should go with pause;lfence now and see if we want to add argument
> eventually.
> HJ, does it sound OK?
Yes, I am checking a patch to default to "pause; lfence".
--
H.J.
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
2018-01-14 10:45 ` Jan Hubicka
2018-01-14 12:33 ` Uros Bizjak
@ 2018-01-14 14:22 ` Jan Hubicka
2018-01-14 14:30 ` H.J. Lu
1 sibling, 1 reply; 120+ messages in thread
From: Jan Hubicka @ 2018-01-14 14:22 UTC (permalink / raw)
To: Kumar, Venkataramanan
Cc: H.J. Lu, gcc-patches, Dharmakan, Rohit arul raj, Nagarajan,
Muthu kumar raj, Uros Bizjak (ubizjak@gmail.com)
> > Hi HJ,
> >
> > > -----Original Message-----
> > > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
> > > owner@gcc.gnu.org] On Behalf Of H.J. Lu
> > > Sent: Sunday, January 14, 2018 9:07 AM
> > > To: gcc-patches@gcc.gnu.org
> > > Subject: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
> > >
> > > This set of patches for GCC 8 mitigates variant #2 of the speculative
> > > execution vulnerabilities on x86 processors identified by CVE-2017-5715, aka
> > > Spectre. They convert indirect branches and function returns to call and
> > > return thunks to avoid speculative execution via indirect call, jmp and ret.
> > >
> > > H.J. Lu (5):
> > > x86: Add -mindirect-branch=
> > > x86: Add -mfunction-return=
> > > x86: Add -mindirect-branch-register
> > > x86: Add 'V' register operand modifier
> > > x86: Disallow -mindirect-branch=/-mfunction-return= with
> > > -mcmodel=large
> >
> > Current set of patches don't seem to have any option to generate "lfence" as the loop filler in "retpoline",  which is required by AMD.
> > Can you please clarify the plan. We would like to get this checked-in GCC 8.Â
>
> Since thunks are output as strings, it is easy to add the option
> on the top of patch #1 of the series. I do not fully understand
> the reason for choosing pause over lfence for Intel, but if we need
> to do both, we need to have command line option (and possibly attribute).
> What would be reasonable name for it?
I forgot there is -mindirect-branch-loop for that in the original patchset.
So for now we should be happy with having both lfence and pause in there
or do we still need it?
Honza
>
> Honza
^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
2018-01-14 14:22 ` Jan Hubicka
@ 2018-01-14 14:30 ` H.J. Lu
2018-01-15 6:02 ` Kumar, Venkataramanan
0 siblings, 1 reply; 120+ messages in thread
From: H.J. Lu @ 2018-01-14 14:30 UTC (permalink / raw)
To: Jan Hubicka
Cc: Kumar, Venkataramanan, gcc-patches, Dharmakan, Rohit arul raj,
Nagarajan, Muthu kumar raj, Uros Bizjak (ubizjak@gmail.com)
On Sun, Jan 14, 2018 at 6:20 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> > Hi HJ,
>> >
>> > > -----Original Message-----
>> > > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
>> > > owner@gcc.gnu.org] On Behalf Of H.J. Lu
>> > > Sent: Sunday, January 14, 2018 9:07 AM
>> > > To: gcc-patches@gcc.gnu.org
>> > > Subject: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
>> > >
>> > > This set of patches for GCC 8 mitigates variant #2 of the speculative
>> > > execution vulnerabilities on x86 processors identified by CVE-2017-5715, aka
>> > > Spectre. They convert indirect branches and function returns to call and
>> > > return thunks to avoid speculative execution via indirect call, jmp and ret.
>> > >
>> > > H.J. Lu (5):
>> > > x86: Add -mindirect-branch=
>> > > x86: Add -mfunction-return=
>> > > x86: Add -mindirect-branch-register
>> > > x86: Add 'V' register operand modifier
>> > > x86: Disallow -mindirect-branch=/-mfunction-return= with
>> > > -mcmodel=large
>> >
>> > Current set of patches don't seem to have any option to generate "lfence" as the loop filler in "retpoline", which is required by AMD.
>> > Can you please clarify the plan. We would like to get this checked-in GCC 8.
>>
>> Since thunks are output as strings, it is easy to add the option
>> on the top of patch #1 of the series. I do not fully understand
>> the reason for choosing pause over lfence for Intel, but if we need
>> to do both, we need to have command line option (and possibly attribute).
>> What would be reasonable name for it?
>
> I forgot there is -mindirect-branch-loop for that in the original patchset.
> So for now we should be happy with having both lfence and pause in there
> or do we still need it?
>
I suggest we leave it out for the time being.
--
H.J.
^ permalink raw reply [flat|nested] 120+ messages in thread
* RE: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
2018-01-14 14:30 ` H.J. Lu
@ 2018-01-15 6:02 ` Kumar, Venkataramanan
0 siblings, 0 replies; 120+ messages in thread
From: Kumar, Venkataramanan @ 2018-01-15 6:02 UTC (permalink / raw)
To: H.J. Lu, Jan Hubicka
Cc: gcc-patches, Dharmakan, Rohit arul raj, Nagarajan,
Muthu kumar raj, Uros Bizjak (ubizjak@gmail.com)
Hi
> -----Original Message-----
> From: H.J. Lu [mailto:hjl.tools@gmail.com]
> Sent: Sunday, January 14, 2018 7:52 PM
> To: Jan Hubicka <hubicka@ucw.cz>
> Cc: Kumar, Venkataramanan <Venkataramanan.Kumar@amd.com>; gcc-
> patches@gcc.gnu.org; Dharmakan, Rohit arul raj
> <Rohitarulraj.Dharmakan@amd.com>; Nagarajan, Muthu kumar raj
> <Muthukumarraj.Nagarajan@amd.com>; Uros Bizjak (ubizjak@gmail.com)
> <ubizjak@gmail.com>
> Subject: Re: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
>
> On Sun, Jan 14, 2018 at 6:20 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
> >> > Hi HJ,
> >> >
> >> > > -----Original Message-----
> >> > > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
> >> > > owner@gcc.gnu.org] On Behalf Of H.J. Lu
> >> > > Sent: Sunday, January 14, 2018 9:07 AM
> >> > > To: gcc-patches@gcc.gnu.org
> >> > > Subject: [PATCH 0/5] x86: CVE-2017-5715, aka Spectre
> >> > >
> >> > > This set of patches for GCC 8 mitigates variant #2 of the
> >> > > speculative execution vulnerabilities on x86 processors
> >> > > identified by CVE-2017-5715, aka Spectre. They convert indirect
> >> > > branches and function returns to call and return thunks to avoid
> speculative execution via indirect call, jmp and ret.
> >> > >
> >> > > H.J. Lu (5):
> >> > > x86: Add -mindirect-branch=
> >> > > x86: Add -mfunction-return=
> >> > > x86: Add -mindirect-branch-register
> >> > > x86: Add 'V' register operand modifier
> >> > > x86: Disallow -mindirect-branch=/-mfunction-return= with
> >> > > -mcmodel=large
> >> >
> >> > Current set of patches don't seem to have any option to generate
> "lfence" as the loop filler in "retpoline", which is required by AMD.
> >> > Can you please clarify the plan. We would like to get this checked-in GCC
> 8.
> >>
> >> Since thunks are output as strings, it is easy to add the option on
> >> the top of patch #1 of the series. I do not fully understand the
> >> reason for choosing pause over lfence for Intel, but if we need to do
> >> both, we need to have command line option (and possibly attribute).
> >> What would be reasonable name for it?
> >
> > I forgot there is -mindirect-branch-loop for that in the original patchset.
> > So for now we should be happy with having both lfence and pause in
> > there or do we still need it?
> >
>
> I suggest we leave it out for the time being.
Yes as of now having both "lfence" and "pause" is Ok. Hope we can add "- indirect-branch-loop" option later if required.
Regards,
Venkat,
>
>
> --
> H.J.
^ permalink raw reply [flat|nested] 120+ messages in thread