diff mbox series

[middle-end,i386,version,6] Add -fzero-call-used-regs=[skip|used-gpr-arg|used-arg|all-gpr-arg|all-arg|used-gpr|all-gpr|used|all]

Message ID A17C7FBE-4259-41CD-A039-4A6ECEC09619@ORACLE.COM
State New
Headers show
Series [middle-end,i386,version,6] Add -fzero-call-used-regs=[skip|used-gpr-arg|used-arg|all-gpr-arg|all-arg|used-gpr|all-gpr|used|all] | expand

Commit Message

Qing Zhao Oct. 29, 2020, 11:52 p.m. UTC
Hi, 

This is the 6th version of the implementation of patch -fzero-call-used-regs.

The X86 part is exactly the same as 5th version. (And it has been approved by Uros already).

The major change compared to the previous version (5th version) are:

1. Documentation change per Richard’s suggestion;
2. Other minor changes. 
3.  general testing cases update per Richard’s suggestion;


I have tested this new GCC on both x86 and arm64, no regression. 

Richard, Please let me know whether it’s ready for stage 1 gcc11?

thanks.

Qing

In order to make it easier for you to review the change, I list the changes I added compared to the 5th version first, and then the whole patch followed.

*****The patch compared to 5th version:

From 3545cc92b327e11af0dde832a60161da92cc4262 Mon Sep 17 00:00:00 2001
From: qing zhao <qinzhao@gcc.gnu.org>
Date: Thu, 29 Oct 2020 18:40:27 +0100
Subject: [PATCH] fix all issues raised by Richard on 10/29

---
 gcc/c-family/c-attribs.c                           |  8 +-
 gcc/common.opt                                     |  2 +-
 gcc/doc/extend.texi                                | 72 ++++++++---------
 gcc/doc/invoke.texi                                |  6 +-
 gcc/emit-rtl.h                                     |  3 -
 gcc/flag-types.h                                   |  2 +-
 gcc/function.c                                     | 56 +++++++-------
 gcc/opts.c                                         |  2 +-
 gcc/targhooks.c                                    |  2 +-
 gcc/testsuite/c-c++-common/zero-scratch-regs-11.c  | 90 +---------------------
 gcc/testsuite/c-c++-common/zero-scratch-regs-2.c   | 13 +---
 gcc/testsuite/c-c++-common/zero-scratch-regs-3.c   | 13 +---
 gcc/testsuite/c-c++-common/zero-scratch-regs-4.c   | 13 +---
 gcc/testsuite/c-c++-common/zero-scratch-regs-5.c   | 13 +---
 gcc/testsuite/c-c++-common/zero-scratch-regs-6.c   | 13 +---
 gcc/testsuite/c-c++-common/zero-scratch-regs-7.c   | 13 +---
 gcc/testsuite/c-c++-common/zero-scratch-regs-8.c   | 13 +---
 gcc/testsuite/c-c++-common/zero-scratch-regs-9.c   | 13 +---
 .../c-c++-common/zero-scratch-regs-attr-usages.c   | 10 ++-
 19 files changed, 90 insertions(+), 267 deletions(-)

Comments

Richard Sandiford Oct. 30, 2020, 9:54 a.m. UTC | #1
Qing Zhao <QING.ZHAO@ORACLE.COM> writes:
> @@ -3996,22 +3996,19 @@ with a named @code{target} must be @code{static}.
>  @cindex @code{zero_call_used_regs} function attribute
>  
>  The @code{zero_call_used_regs} attribute causes the compiler to zero
> -a subset of all call-used registers at function return according to
> -@var{choice}.
> -This is used to increase the program security by either mitigating
> -Return-Oriented Programming (ROP) or preventing information leak
> +a subset of all call-used registers@footnote{A ``call-used'' register
> +is a register whose contents can be changed by a function call;
> +therefore, a caller cannot assume that the register has the same contents
> +on return from the function as it had before calling the function.  Such
> +registers are also called ``call-clobbered'', ``caller-saved'', or
> +``volatile''.} at function return.
> +This is used to increase program security by either mitigating
> +Return-Oriented Programming (ROP) attacks or preventing information leakage
>  through registers.
>  
> -A ``call-used'' register is a register whose contents can be changed by
> -a function call; therefore, a caller cannot assume that the register has
> -the same contents on return from the function as it had before calling
> -the function.  Such registers are also called ``call-clobbered'',
> -``caller-saved'', or ``volatile''.
> -
>  In order to satisfy users with different security needs and control the
> -run-time overhead at the same time, GCC provides a flexible way to choose
> -the subset of the call-used registers to be zeroed.
> -
> +run-time overhead at the same time, @var{choice} parameter provides a

I suggested “the @var{choice} parameter provides” in the review yesterday.
The “the” is needed.

> +flexible way to choose the subset of the call-used registers to be zeroed.
>  The three basic values of @var{choice} are:
>  
>  @itemize @bullet
> @@ -4046,42 +4043,41 @@ together, they must appear in the order above.
>  
>  The full list of @var{choice}s is therefore:
>  
> -@itemize @bullet
> -@item
> -@samp{skip} doesn't zero any call-used register.
> +@table @code
> +@item skip
> +doesn't zero any call-used register.
>  
> -@item
> -@samp{used} only zeros call-used registers that are used in the function.
> +@item used
> +only zeros call-used registers that are used in the function.
>  
> -@item
> -@samp{all} zeros all call-used registers.
> +@item used-gpr
> +only zeros call-used general purpose registers that are used in the function.
>  
> -@item
> -@samp{used-arg} only zeros used call-used registers that pass arguments.
> +@item used-arg
> +only zeros call-used registers that are used in the function and pass arguments.
>  
> -@item
> -@samp{used-gpr} only zeros used call-used general purpose registers.
> +@item used-gpr-arg
> +only zeros call-used general purpose registers that are used in the function
> +and pass arguments.
>  
> -@item
> -@samp{used-gpr-arg} only zeros used call-used general purpose registers that
> -pass arguments.
> +@item all
> +zeros all call-used registers.
>  
> -@item
> -@samp{all-gpr-arg} zeros all call-used general purpose registers that pass
> -arguments.
> +@item all-gpr
> +zeros all call-used general purpose registers.
>  
> -@item
> -@samp{all-arg} zeros all call-used registers that pass arguments.
> +@item all-arg
> +zeros all call-used registers that pass arguments.
>  
> -@item
> -@samp{all-gpr} zeros all call-used general purpose registers.
> -@end itemize
> +@item all-gpr-arg
> +zeros all call-used general purpose registers that pass
> +arguments.
> +@end table

TBH I also think the order I suggested yesterday is more natural
than this one, but either's OK.  The above certainly addresses
the original concern I had about the order being inconsistent.

> @@ -288,7 +288,7 @@ enum sanitize_code {
>  };
>  
>  /* Different settings for zeroing subset of registers.  */
> -namespace  zero_regs_code {
> +namespace zero_regs_flag {

I suggested “zero_regs_flags” rather than “zero_reg_flag” yesterday;
I think “zero_regs_flags” is better because the namespace contains
more than one flag.

> @@ -1776,7 +1776,7 @@ const struct sanitizer_opts_s coverage_sanitizer_opts[] =
>    { NULL, 0U, 0UL, false }
>  };
>  
> -using namespace zero_regs_code;
> +using namespace zero_regs_flag;
>  /* -fzero-call-used-regs= suboptions.  */
>  const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
>  {

Sorry, I didn't notice this yesterday.  The table should use
fully-qualified names: zero_regs_flags::SKIP etc.  We shouldn't
do a using namespace for the whole file here.

OK with those changes, and thanks for doing this.

The new tests are likely to fail on some targets with the sorry()
message, but I think target maintainers are best placed to decide
whether (a) that's a fundamental restriction of the target and the
tests should just be skipped or (b) the target needs to implement
the new hook.

Richard
Qing Zhao Oct. 30, 2020, 2:25 p.m. UTC | #2
> On Oct 30, 2020, at 4:54 AM, Richard Sandiford <richard.sandiford@arm.com> wrote:
> 
> Qing Zhao <QING.ZHAO@ORACLE.COM> writes:
>> @@ -3996,22 +3996,19 @@ with a named @code{target} must be @code{static}.
>> @cindex @code{zero_call_used_regs} function attribute
>> 
>> The @code{zero_call_used_regs} attribute causes the compiler to zero
>> -a subset of all call-used registers at function return according to
>> -@var{choice}.
>> -This is used to increase the program security by either mitigating
>> -Return-Oriented Programming (ROP) or preventing information leak
>> +a subset of all call-used registers@footnote{A ``call-used'' register
>> +is a register whose contents can be changed by a function call;
>> +therefore, a caller cannot assume that the register has the same contents
>> +on return from the function as it had before calling the function.  Such
>> +registers are also called ``call-clobbered'', ``caller-saved'', or
>> +``volatile''.} at function return.
>> +This is used to increase program security by either mitigating
>> +Return-Oriented Programming (ROP) attacks or preventing information leakage
>> through registers.
>> 
>> -A ``call-used'' register is a register whose contents can be changed by
>> -a function call; therefore, a caller cannot assume that the register has
>> -the same contents on return from the function as it had before calling
>> -the function.  Such registers are also called ``call-clobbered'',
>> -``caller-saved'', or ``volatile''.
>> -
>> In order to satisfy users with different security needs and control the
>> -run-time overhead at the same time, GCC provides a flexible way to choose
>> -the subset of the call-used registers to be zeroed.
>> -
>> +run-time overhead at the same time, @var{choice} parameter provides a
> 
> I suggested “the @var{choice} parameter provides” in the review yesterday.
> The “the” is needed.
My bad, added it.
> 
>> +flexible way to choose the subset of the call-used registers to be zeroed.
>> The three basic values of @var{choice} are:
>> 
>> @itemize @bullet
>> @@ -4046,42 +4043,41 @@ together, they must appear in the order above.
>> 
>> The full list of @var{choice}s is therefore:
>> 
>> -@itemize @bullet
>> -@item
>> -@samp{skip} doesn't zero any call-used register.
>> +@table @code
>> +@item skip
>> +doesn't zero any call-used register.
>> 
>> -@item
>> -@samp{used} only zeros call-used registers that are used in the function.
>> +@item used
>> +only zeros call-used registers that are used in the function.
>> 
>> -@item
>> -@samp{all} zeros all call-used registers.
>> +@item used-gpr
>> +only zeros call-used general purpose registers that are used in the function.
>> 
>> -@item
>> -@samp{used-arg} only zeros used call-used registers that pass arguments.
>> +@item used-arg
>> +only zeros call-used registers that are used in the function and pass arguments.
>> 
>> -@item
>> -@samp{used-gpr} only zeros used call-used general purpose registers.
>> +@item used-gpr-arg
>> +only zeros call-used general purpose registers that are used in the function
>> +and pass arguments.
>> 
>> -@item
>> -@samp{used-gpr-arg} only zeros used call-used general purpose registers that
>> -pass arguments.
>> +@item all
>> +zeros all call-used registers.
>> 
>> -@item
>> -@samp{all-gpr-arg} zeros all call-used general purpose registers that pass
>> -arguments.
>> +@item all-gpr
>> +zeros all call-used general purpose registers.
>> 
>> -@item
>> -@samp{all-arg} zeros all call-used registers that pass arguments.
>> +@item all-arg
>> +zeros all call-used registers that pass arguments.
>> 
>> -@item
>> -@samp{all-gpr} zeros all call-used general purpose registers.
>> -@end itemize
>> +@item all-gpr-arg
>> +zeros all call-used general purpose registers that pass
>> +arguments.
>> +@end table
> 
> TBH I also think the order I suggested yesterday is more natural
> than this one, but either's OK.  The above certainly addresses
> the original concern I had about the order being inconsistent.

You suggested:

- skip
- used
- used-arg
- used-gpr
- used-gpr-arg
- all
- all-arg
- all-gpr
- all-gpr-arg

I changed to: (switched the order used-arg and used-gpr,    all-arg and all-gpr)

-skip
-used
-used-gpr
-used-arg
-used-gpr-arg
-all
-all-gpr
-all-arg
-all-gpr-arg

I intended to do this change in order to make “gpr” before “arg”.

> 
>> @@ -288,7 +288,7 @@ enum sanitize_code {
>> };
>> 
>> /* Different settings for zeroing subset of registers.  */
>> -namespace  zero_regs_code {
>> +namespace zero_regs_flag {
> 
> I suggested “zero_regs_flags” rather than “zero_reg_flag” yesterday;
> I think “zero_regs_flags” is better because the namespace contains
> more than one flag.

Okay.
> 
>> @@ -1776,7 +1776,7 @@ const struct sanitizer_opts_s coverage_sanitizer_opts[] =
>>   { NULL, 0U, 0UL, false }
>> };
>> 
>> -using namespace zero_regs_code;
>> +using namespace zero_regs_flag;
>> /* -fzero-call-used-regs= suboptions.  */
>> const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
>> {
> 
> Sorry, I didn't notice this yesterday.  The table should use
> fully-qualified names: zero_regs_flags::SKIP etc.  We shouldn't
> do a using namespace for the whole file here.
> 
> OK with those changes, and thanks for doing this.

Thanks a lot, I will commit the patch with this set of changes you suggested. 

Really appreciate for all your help and patience. 

> 
> The new tests are likely to fail on some targets with the sorry()
> message, but I think target maintainers are best placed to decide
> whether (a) that's a fundamental restriction of the target and the
> tests should just be skipped or (b) the target needs to implement
> the new hook.

Okay. 

I will commit the patch with the following new change:

Thanks.

Qing

The new diff is:
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 25b39094410..9091b72f5c3 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4007,7 +4007,7 @@ Return-Oriented Programming (ROP) attacks or preventing information leakage
 through registers.
 
 In order to satisfy users with different security needs and control the
-run-time overhead at the same time, @var{choice} parameter provides a
+run-time overhead at the same time, the @var{choice} parameter provides a
 flexible way to choose the subset of the call-used registers to be zeroed.
 The three basic values of @var{choice} are:
 
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 51c7e5f369b..9ef629b28af 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -288,7 +288,7 @@ enum sanitize_code {
 };
 
 /* Different settings for zeroing subset of registers.  */
-namespace zero_regs_flag {
+namespace zero_regs_flags {
   const unsigned int UNSET = 0;
   const unsigned int SKIP = 1UL << 0;
   const unsigned int ONLY_USED = 1UL << 1;
diff --git a/gcc/function.c b/gcc/function.c
index 76a503728e8..15249450592 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -5842,7 +5842,7 @@ gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type)
      if only_arg is true, only zero call-used registers that pass
      parameters defined by the flatform's calling conversion.  */
 
-  using namespace zero_regs_flag;
+  using namespace zero_regs_flags;
 
   only_gpr = zero_regs_type & ONLY_GPR;
   only_used = zero_regs_type & ONLY_USED;
@@ -6616,7 +6616,7 @@ public:
 unsigned int
 pass_zero_call_used_regs::execute (function *fun)
 {
-  using namespace zero_regs_flag;
+  using namespace zero_regs_flags;
   unsigned int zero_regs_type = UNSET;
 
   tree attr_zero_regs = lookup_attribute ("zero_call_used_regs",
diff --git a/gcc/opts.c b/gcc/opts.c
index 8c439e4006f..7e91c6c5a82 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1776,21 +1776,20 @@ const struct sanitizer_opts_s coverage_sanitizer_opts[] =
   { NULL, 0U, 0UL, false }
 };
 
-using namespace zero_regs_flag;
 /* -fzero-call-used-regs= suboptions.  */
 const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
 {
 #define ZERO_CALL_USED_REGS_OPT(name, flags) \
     { #name, flags }
-  ZERO_CALL_USED_REGS_OPT (skip, SKIP),
-  ZERO_CALL_USED_REGS_OPT (used-gpr-arg, USED_GPR_ARG),
-  ZERO_CALL_USED_REGS_OPT (used-gpr, USED_GPR),
-  ZERO_CALL_USED_REGS_OPT (used-arg, USED_ARG),
-  ZERO_CALL_USED_REGS_OPT (used, USED),
-  ZERO_CALL_USED_REGS_OPT (all-gpr-arg, ALL_GPR_ARG),
-  ZERO_CALL_USED_REGS_OPT (all-gpr, ALL_GPR),
-  ZERO_CALL_USED_REGS_OPT (all-arg, ALL_ARG),
-  ZERO_CALL_USED_REGS_OPT (all, ALL),
+  ZERO_CALL_USED_REGS_OPT (skip, zero_regs_flags::SKIP),
+  ZERO_CALL_USED_REGS_OPT (used-gpr-arg, zero_regs_flags::USED_GPR_ARG),
+  ZERO_CALL_USED_REGS_OPT (used-gpr, zero_regs_flags::USED_GPR),
+  ZERO_CALL_USED_REGS_OPT (used-arg, zero_regs_flags::USED_ARG),
+  ZERO_CALL_USED_REGS_OPT (used, zero_regs_flags::USED),
+  ZERO_CALL_USED_REGS_OPT (all-gpr-arg, zero_regs_flags::ALL_GPR_ARG),
+  ZERO_CALL_USED_REGS_OPT (all-gpr, zero_regs_flags::ALL_GPR),
+  ZERO_CALL_USED_REGS_OPT (all-arg, zero_regs_flags::ALL_ARG),
+  ZERO_CALL_USED_REGS_OPT (all, zero_regs_flags::ALL),
 #undef ZERO_CALL_USED_REGS_OPT
   {NULL, 0U}
 };

> 
> Richard
Qing Zhao Oct. 30, 2020, 7:50 p.m. UTC | #3
FYI.

I just committed the patch to gcc11 as:

https://gcc.gnu.org/pipermail/gcc-cvs/2020-October/336263.html <https://gcc.gnu.org/pipermail/gcc-cvs/2020-October/336263.html>

Qing
Tobias Burnus Nov. 4, 2020, 10:54 a.m. UTC | #4
Three of the testcases fail on PowerPC: gcc.target/i386/zero-scratch-regs-{9,10,11}.c
   powerpc64le-linux-gnu/default/gcc.d/zero-scratch-regs-10.c:77:1: sorry, unimplemented: '-fzero-call-used_regs' not supported on this target

Did you miss some dg-require-effective-target ?

powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -Wc++-compat  (test for excess errors)
powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -Wc++-compat  (test for excess errors)
powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -Wc++-compat  (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++98 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++14 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++17 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++2a (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++98 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++14 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++17 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++2a (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -std=gnu++98 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -std=gnu++14 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -std=gnu++17 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -std=gnu++2a (test for excess errors)

Tobias

On 30.10.20 20:50, Qing Zhao via Gcc-patches wrote:

> FYI.
>
> I just committed the patch to gcc11 as:
>
> https://gcc.gnu.org/pipermail/gcc-cvs/2020-October/336263.html <https://gcc.gnu.org/pipermail/gcc-cvs/2020-October/336263.html>
>
> Qing
-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
Christophe Lyon Nov. 4, 2020, 1:08 p.m. UTC | #5
On Wed, 4 Nov 2020 at 11:54, Tobias Burnus <tobias@codesourcery.com> wrote:
>
> Three of the testcases fail on PowerPC: gcc.target/i386/zero-scratch-regs-{9,10,11}.c
>    powerpc64le-linux-gnu/default/gcc.d/zero-scratch-regs-10.c:77:1: sorry, unimplemented: '-fzero-call-used_regs' not supported on this target
>
> Did you miss some dg-require-effective-target ?
>
> powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -Wc++-compat  (test for excess errors)
> powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -Wc++-compat  (test for excess errors)
> powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -Wc++-compat  (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++98 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++14 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++17 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++2a (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++98 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++14 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++17 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++2a (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -std=gnu++98 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -std=gnu++14 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -std=gnu++17 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -std=gnu++2a (test for excess errors)
>

This was reported as PR97680, see also PR97699 ofr arm.

> Tobias
>
> On 30.10.20 20:50, Qing Zhao via Gcc-patches wrote:
>
> > FYI.
> >
> > I just committed the patch to gcc11 as:
> >
> > https://gcc.gnu.org/pipermail/gcc-cvs/2020-October/336263.html <https://gcc.gnu.org/pipermail/gcc-cvs/2020-October/336263.html>
> >
> > Qing
> -----------------
> Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
> Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
Richard Sandiford Nov. 4, 2020, 1:20 p.m. UTC | #6
Tobias Burnus <tobias@codesourcery.com> writes:
> Three of the testcases fail on PowerPC: gcc.target/i386/zero-scratch-regs-{9,10,11}.c
>    powerpc64le-linux-gnu/default/gcc.d/zero-scratch-regs-10.c:77:1: sorry, unimplemented: '-fzero-call-used_regs' not supported on this target
>
> Did you miss some dg-require-effective-target ?

No, these are a signal to target maintainers that they need
to decide whether to add support or accept the status quo
(in which case a new effective-target will be needed).  See:
https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557595.html:

    The new tests are likely to fail on some targets with the sorry()
    message, but I think target maintainers are best placed to decide
    whether (a) that's a fundamental restriction of the target and the
    tests should just be skipped or (b) the target needs to implement
    the new hook.

Thanks,
Richard
Segher Boessenkool Nov. 4, 2020, 7 p.m. UTC | #7
On Wed, Nov 04, 2020 at 01:20:58PM +0000, Richard Sandiford wrote:
> Tobias Burnus <tobias@codesourcery.com> writes:
> > Three of the testcases fail on PowerPC: gcc.target/i386/zero-scratch-regs-{9,10,11}.c
> >    powerpc64le-linux-gnu/default/gcc.d/zero-scratch-regs-10.c:77:1: sorry, unimplemented: '-fzero-call-used_regs' not supported on this target
> >
> > Did you miss some dg-require-effective-target ?
> 
> No, these are a signal to target maintainers that they need
> to decide whether to add support or accept the status quo
> (in which case a new effective-target will be needed).  See:
> https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557595.html:
> 
>     The new tests are likely to fail on some targets with the sorry()
>     message, but I think target maintainers are best placed to decide
>     whether (a) that's a fundamental restriction of the target and the
>     tests should just be skipped or (b) the target needs to implement
>     the new hook.

But why are tests in gcc.target/i386/ run for other targets at all?!


Segher
Qing Zhao Nov. 4, 2020, 7:58 p.m. UTC | #8
> On Nov 4, 2020, at 1:00 PM, Segher Boessenkool <segher@kernel.crashing.org> wrote:
> 
> On Wed, Nov 04, 2020 at 01:20:58PM +0000, Richard Sandiford wrote:
>> Tobias Burnus <tobias@codesourcery.com> writes:
>>> Three of the testcases fail on PowerPC: gcc.target/i386/zero-scratch-regs-{9,10,11}.c
>>>   powerpc64le-linux-gnu/default/gcc.d/zero-scratch-regs-10.c:77:1: sorry, unimplemented: '-fzero-call-used_regs' not supported on this target
>>> 
>>> Did you miss some dg-require-effective-target ?
>> 
>> No, these are a signal to target maintainers that they need
>> to decide whether to add support or accept the status quo
>> (in which case a new effective-target will be needed).  See:
>> https://urldefense.com/v3/__https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557595.html__;!!GqivPVa7Brio!PD1t9rpXf7lNS8yVbiQckiR5w3bv1eqGZenzRGPMBTAlYpshdQ9qVR0JLhoeNFMg$ :
>> 
>>    The new tests are likely to fail on some targets with the sorry()
>>    message, but I think target maintainers are best placed to decide
>>    whether (a) that's a fundamental restriction of the target and the
>>    tests should just be skipped or (b) the target needs to implement
>>    the new hook.
> 
> But why are tests in gcc.target/i386/ run for other targets at all?!

No,  tests in gcc.target/i386 should not run for PowerPC.

What Tobias Burnus mentioned are the following tests:

powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -Wc++-compat  (test for excess errors)
powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -Wc++-compat  (test for excess errors)
powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -Wc++-compat  (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++98 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++14 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++17 (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++2a (test for excess errors)
powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++98 (test for excess errors)


They are under c-c++-common, not gcc.target/i386. 

These testing cases are added intentionaly on all platforms in order to check whether  the current middle-end default implementation for
-fzero-call-used-regs works on the specific platform.

If the default implementation doesn’t work for the specific platform, for example, on PowerPC, it’s better for the Maintainer of PowerPC to decide
Whether to skip these testing case on this platform or add a PowerPC implementation.

Qing
> 
> 
> Segher
Segher Boessenkool Nov. 4, 2020, 10:01 p.m. UTC | #9
On Wed, Nov 04, 2020 at 01:58:26PM -0600, Qing Zhao wrote:
> > On Nov 4, 2020, at 1:00 PM, Segher Boessenkool <segher@kernel.crashing.org> wrote:
> > On Wed, Nov 04, 2020 at 01:20:58PM +0000, Richard Sandiford wrote:
> >> Tobias Burnus <tobias@codesourcery.com> writes:
> >>> Three of the testcases fail on PowerPC: gcc.target/i386/zero-scratch-regs-{9,10,11}.c
> >>>   powerpc64le-linux-gnu/default/gcc.d/zero-scratch-regs-10.c:77:1: sorry, unimplemented: '-fzero-call-used_regs' not supported on this target
> >>> 
> >>> Did you miss some dg-require-effective-target ?
> >> 
> >> No, these are a signal to target maintainers that they need
> >> to decide whether to add support or accept the status quo
> >> (in which case a new effective-target will be needed).  See:
> >> https://urldefense.com/v3/__https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557595.html__;!!GqivPVa7Brio!PD1t9rpXf7lNS8yVbiQckiR5w3bv1eqGZenzRGPMBTAlYpshdQ9qVR0JLhoeNFMg$ :
> >> 
> >>    The new tests are likely to fail on some targets with the sorry()
> >>    message, but I think target maintainers are best placed to decide
> >>    whether (a) that's a fundamental restriction of the target and the
> >>    tests should just be skipped or (b) the target needs to implement
> >>    the new hook.
> > 
> > But why are tests in gcc.target/i386/ run for other targets at all?!
> 
> No,  tests in gcc.target/i386 should not run for PowerPC.
> 
> What Tobias Burnus mentioned are the following tests:
> 
> powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -Wc++-compat  (test for excess errors)
> powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -Wc++-compat  (test for excess errors)
> powerpc64le-linux-gnu-gcc.sum:FAIL: c-c++-common/zero-scratch-regs-9.c  -Wc++-compat  (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++98 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++14 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++17 (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-10.c  -std=gnu++2a (test for excess errors)
> powerpc64le-linux-gnu-g++.sum:FAIL: c-c++-common/zero-scratch-regs-11.c  -std=gnu++98 (test for excess errors)
> 
> 
> They are under c-c++-common, not gcc.target/i386. 

Ah, good.  But the mail said

> >>> Three of the testcases fail on PowerPC: gcc.target/i386/zero-scratch-regs-{9,10,11}.c
> >>>   powerpc64le-linux-gnu/default/gcc.d/zero-scratch-regs-10.c:77:1: sorry, unimplemented: '-fzero-call-used_regs' not supported on this target

so :-)

> These testing cases are added intentionaly on all platforms in order to check whether  the current middle-end default implementation for
> -fzero-call-used-regs works on the specific platform.
> 
> If the default implementation doesn’t work for the specific platform, for example, on PowerPC, it’s better for the Maintainer of PowerPC to decide
> Whether to skip these testing case on this platform or add a PowerPC implementation.

Yeah, we will deal with it.  In stage 3 :-)


Segher
Tobias Burnus Nov. 4, 2020, 10:21 p.m. UTC | #10
On 04.11.20 20:00, Segher Boessenkool wrote:
> But why are tests in gcc.target/i386/ run for other targets at all?!

Those under gcc.target/i386/ contain assembler checks – and are only
for x86 and are (hence) also only suitable for x86.

But the failing ones for PowerPC (PR97680) and ARM (PR97699) are under
c-c++-common/zero-scratch-regs-*.c (only {9,10,11} fail here, I did
see a "8" fail at gcc-testresults). And those tests apply to all targets.

On 04.11.20 23:01, Segher Boessenkool wrote:
>> If the default implementation doesn’t work ... on PowerPC, ... the Maintainer ... to decide
>> Whether to skip these testing case on this platform or add a PowerPC implementation.
> Yeah, we will deal with it.  In stage 3:-)

Thanks!

Tobias

-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
diff mbox series

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 1b05e8c..8da1dc7 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -4979,13 +4979,15 @@  handle_zero_call_used_regs_attribute (tree *node, tree name, tree args,
       error_at (DECL_SOURCE_LOCATION (decl),
 		"%qE attribute applies only to functions", name);
       *no_add_attrs = true;
+      return NULL_TREE;
     }
 
   if (TREE_CODE (id) != STRING_CST)
     {
       error_at (DECL_SOURCE_LOCATION (decl),
-		"attribute %qE arguments not a string", name);
+		"%qE argument not a string", name);
       *no_add_attrs = true;
+      return NULL_TREE;
     }
 
   bool found = false;
@@ -5000,8 +5002,8 @@  handle_zero_call_used_regs_attribute (tree *node, tree name, tree args,
   if (!found)
     {
       error_at (DECL_SOURCE_LOCATION (decl),
-		"unrecognized zero_call_used_regs attribute: %qs",
-		TREE_STRING_POINTER (id));
+		"unrecognized %qE attribute argument %qs",
+		name, TREE_STRING_POINTER (id));
       *no_add_attrs = true;
     }
 
diff --git a/gcc/common.opt b/gcc/common.opt
index 4a13f32..d716ea1 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -228,7 +228,7 @@  unsigned int flag_sanitize_coverage
 Variable
 bool dump_base_name_prefixed = false
 
-; What subset of registers should be zeroed
+; What subset of registers should be zeroed on function return
 Variable
 unsigned int flag_zero_call_used_regs
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index b011c17..25b3909 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3996,22 +3996,19 @@  with a named @code{target} must be @code{static}.
 @cindex @code{zero_call_used_regs} function attribute
 
 The @code{zero_call_used_regs} attribute causes the compiler to zero
-a subset of all call-used registers at function return according to
-@var{choice}.
-This is used to increase the program security by either mitigating
-Return-Oriented Programming (ROP) or preventing information leak
+a subset of all call-used registers@footnote{A ``call-used'' register
+is a register whose contents can be changed by a function call;
+therefore, a caller cannot assume that the register has the same contents
+on return from the function as it had before calling the function.  Such
+registers are also called ``call-clobbered'', ``caller-saved'', or
+``volatile''.} at function return.
+This is used to increase program security by either mitigating
+Return-Oriented Programming (ROP) attacks or preventing information leakage
 through registers.
 
-A ``call-used'' register is a register whose contents can be changed by
-a function call; therefore, a caller cannot assume that the register has
-the same contents on return from the function as it had before calling
-the function.  Such registers are also called ``call-clobbered'',
-``caller-saved'', or ``volatile''.
-
 In order to satisfy users with different security needs and control the
-run-time overhead at the same time, GCC provides a flexible way to choose
-the subset of the call-used registers to be zeroed.
-
+run-time overhead at the same time, @var{choice} parameter provides a
+flexible way to choose the subset of the call-used registers to be zeroed.
 The three basic values of @var{choice} are:
 
 @itemize @bullet
@@ -4046,42 +4043,41 @@  together, they must appear in the order above.
 
 The full list of @var{choice}s is therefore:
 
-@itemize @bullet
-@item
-@samp{skip} doesn't zero any call-used register.
+@table @code
+@item skip
+doesn't zero any call-used register.
 
-@item
-@samp{used} only zeros call-used registers that are used in the function.
+@item used
+only zeros call-used registers that are used in the function.
 
-@item
-@samp{all} zeros all call-used registers.
+@item used-gpr
+only zeros call-used general purpose registers that are used in the function.
 
-@item
-@samp{used-arg} only zeros used call-used registers that pass arguments.
+@item used-arg
+only zeros call-used registers that are used in the function and pass arguments.
 
-@item
-@samp{used-gpr} only zeros used call-used general purpose registers.
+@item used-gpr-arg
+only zeros call-used general purpose registers that are used in the function
+and pass arguments.
 
-@item
-@samp{used-gpr-arg} only zeros used call-used general purpose registers that
-pass arguments.
+@item all
+zeros all call-used registers.
 
-@item
-@samp{all-gpr-arg} zeros all call-used general purpose registers that pass
-arguments.
+@item all-gpr
+zeros all call-used general purpose registers.
 
-@item
-@samp{all-arg} zeros all call-used registers that pass arguments.
+@item all-arg
+zeros all call-used registers that pass arguments.
 
-@item
-@samp{all-gpr} zeros all call-used general purpose registers.
-@end itemize
+@item all-gpr-arg
+zeros all call-used general purpose registers that pass
+arguments.
+@end table
 
-Among this list, @samp{used-gpr-arg}, @samp{used-arg}, @samp{all-gpr-arg},
-and @samp{all-arg} are mainly used for ROP mitigation.
+Of this list, @samp{used-arg}, @samp{used-gpr-arg}, @samp{all-arg},
+and @samp{all-gpr-arg} are mainly used for ROP mitigation.
 
 The default for the attribute is controlled by @option{-fzero-call-used-regs}.
-
 @end table
 
 @c This is the end of the target-independent attribute table
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index c9e3128..23834a2 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12552,9 +12552,9 @@  Not all targets support this option.
 
 @item -fzero-call-used-regs=@var{choice}
 @opindex fzero-call-used-regs
-Zero call-used registers at function return to increase the program
-security by either mitigating Return-Oriented Programming (ROP) or
-preventing information leak through registers.
+Zero call-used registers at function return to increase program
+security by either mitigating Return-Oriented Programming (ROP)
+attacks or preventing information leakage through registers.
 
 The possible values of @var{choice} are the same as for the
 @code{zero_call_used_regs} attribute (@pxref{Function Attributes}).
diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h
index d7bdb66..4658c4a 100644
--- a/gcc/emit-rtl.h
+++ b/gcc/emit-rtl.h
@@ -173,9 +173,6 @@  struct GTY(()) rtl_data {
         local stack.  */
   unsigned int stack_alignment_estimated;
 
-  /* How to zero call-used regsiters for this routine.  */
-  unsigned int zero_call_used_regs;
-
   /* How many NOP insns to place at each function entry by default.  */
   unsigned short patch_area_size;
 
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 8807635..51c7e5f 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -288,7 +288,7 @@  enum sanitize_code {
 };
 
 /* Different settings for zeroing subset of registers.  */
-namespace  zero_regs_code {
+namespace zero_regs_flag {
   const unsigned int UNSET = 0;
   const unsigned int SKIP = 1UL << 0;
   const unsigned int ONLY_USED = 1UL << 1;
diff --git a/gcc/function.c b/gcc/function.c
index 77f0b5d..76a5037 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -5817,15 +5817,15 @@  make_prologue_seq (void)
   return seq;
 }
 
-/* Emit a sequence of insns to zero the call-used registers before RET.  */
-using namespace zero_regs_code;
+/* Emit a sequence of insns to zero the call-used registers before RET
+   according to ZERO_REGS_TYPE.  */
 
 static void
-gen_call_used_regs_seq (rtx_insn *ret)
+gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type)
 {
-  bool gpr_only = true;
-  bool used_only = true;
-  bool arg_only = true;
+  bool only_gpr = true;
+  bool only_used = true;
+  bool only_arg = true;
 
   /* No need to zero call-used-regs in main ().  */
   if (MAIN_NAME_P (DECL_NAME (current_function_decl)))
@@ -5836,23 +5836,25 @@  gen_call_used_regs_seq (rtx_insn *ret)
   if (crtl->calls_eh_return)
     return;
 
-  /* If gpr_only is true, only zero call-used registers that are
-     general-purpose registers; if used_only is true, only zero
+  /* If only_gpr is true, only zero call-used registers that are
+     general-purpose registers; if only_used is true, only zero
      call-used registers that are used in the current function;
-     if arg_only is true, only zero call-used registers that pass
+     if only_arg is true, only zero call-used registers that pass
      parameters defined by the flatform's calling conversion.  */
 
-  gpr_only = crtl->zero_call_used_regs & ONLY_GPR;
-  used_only = crtl->zero_call_used_regs & ONLY_USED;
-  arg_only = crtl->zero_call_used_regs & ONLY_ARG;
+  using namespace zero_regs_flag;
+
+  only_gpr = zero_regs_type & ONLY_GPR;
+  only_used = zero_regs_type & ONLY_USED;
+  only_arg = zero_regs_type & ONLY_ARG;
 
   /* For each of the hard registers, we should zero it if:
 	    1. it is a call-used register;
 	and 2. it is not a fixed register;
 	and 3. it is not live at the return of the routine;
-	and 4. it is general registor if gpr_only is true;
-	and 5. it is used in the routine if used_only is true;
-	and 6. it is a register that passes parameter if arg_only is true.  */
+	and 4. it is general registor if only_gpr is true;
+	and 5. it is used in the routine if only_used is true;
+	and 6. it is a register that passes parameter if only_arg is true.  */
 
   /* First, prepare the data flow information.  */
   basic_block bb = BLOCK_FOR_INSN (ret);
@@ -5871,12 +5873,12 @@  gen_call_used_regs_seq (rtx_insn *ret)
 	continue;
       if (REGNO_REG_SET_P (live_out, regno))
 	continue;
-      if (gpr_only
+      if (only_gpr
 	  && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
 	continue;
-      if (used_only && !df_regs_ever_live_p (regno))
+      if (only_used && !df_regs_ever_live_p (regno))
 	continue;
-      if (arg_only && !FUNCTION_ARG_REGNO_P (regno))
+      if (only_arg && !FUNCTION_ARG_REGNO_P (regno))
 	continue;
 
       /* Now this is a register that we might want to zero.  */
@@ -6614,14 +6616,14 @@  public:
 unsigned int
 pass_zero_call_used_regs::execute (function *fun)
 {
+  using namespace zero_regs_flag;
   unsigned int zero_regs_type = UNSET;
-  unsigned int attr_zero_regs_type = UNSET;
 
-  tree attr_zero_regs
-	= lookup_attribute ("zero_call_used_regs",
-			    DECL_ATTRIBUTES (fun->decl));
+  tree attr_zero_regs = lookup_attribute ("zero_call_used_regs",
+					  DECL_ATTRIBUTES (fun->decl));
 
-  /* Get the type of zero_call_used_regs from function attribute.  */
+  /* Get the type of zero_call_used_regs from function attribute.
+     We have filtered out invalid attribute values already at this point.  */
   if (attr_zero_regs)
     {
       /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
@@ -6635,18 +6637,14 @@  pass_zero_call_used_regs::execute (function *fun)
 	if (strcmp (TREE_STRING_POINTER (attr_zero_regs),
 		     zero_call_used_regs_opts[i].name) == 0)
 	  {
-	    attr_zero_regs_type = zero_call_used_regs_opts[i].flag;
+	    zero_regs_type = zero_call_used_regs_opts[i].flag;
  	    break;
 	  }
     }
 
-  zero_regs_type = attr_zero_regs_type;
-
   if (!zero_regs_type)
     zero_regs_type = flag_zero_call_used_regs;
 
-  crtl->zero_call_used_regs = zero_regs_type;
-
   /* No need to zero call-used-regs when no user request is present.  */
   if (!(zero_regs_type & ENABLED))
     return 0;
@@ -6664,7 +6662,7 @@  pass_zero_call_used_regs::execute (function *fun)
     {
       rtx_insn *insn = BB_END (e->src);
       if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn)))
-	gen_call_used_regs_seq (insn);
+	gen_call_used_regs_seq (insn, zero_regs_type);
     }
 
   return 0;
diff --git a/gcc/opts.c b/gcc/opts.c
index 7cd097a..8c439e4 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1776,7 +1776,7 @@  const struct sanitizer_opts_s coverage_sanitizer_opts[] =
   { NULL, 0U, 0UL, false }
 };
 
-using namespace zero_regs_code;
+using namespace zero_regs_flag;
 /* -fzero-call-used-regs= suboptions.  */
 const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
 {
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 88eef00..4e4d100 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1011,7 +1011,7 @@  default_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs)
 	      {
 		issued_error = true;
 		sorry ("%qs not supported on this target",
-			"fzero-call-used_regs");
+			"-fzero-call-used_regs");
 	      }
 	    delete_insns_since (last_insn);
 	  }
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
index 6756f57..7721e39 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
@@ -1,92 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=all" } */
 
-#include <assert.h>
-int result = 0;
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("skip")))
-foo1 (int x)
-{
-  return (x + 1);
-}
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("used-gpr-arg")))
-foo2 (int x)
-{
-  return (x + 2);
-}
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("used-gpr")))
-foo3 (int x)
-{
-  return (x + 3);
-}
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("used-arg")))
-foo4 (int x)
-{
-  return (x + 4);
-}
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("used")))
-foo5 (int x)
-{
-  return (x + 5);
-}
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("all-gpr-arg")))
-foo6 (int x)
-{
-  return (x + 6);
-}
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("all-gpr")))
-foo7 (int x)
-{
-  return (x + 7);
-}
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("all-arg")))
-foo8 (int x)
-{
-  return (x + 8);
-}
-
-int 
-__attribute__((noipa))
-__attribute__ ((zero_call_used_regs("all")))
-foo9 (int x)
-{
-  return (x + 9);
-}
-
-int main()
-{
-  result = foo1 (1);
-  result += foo2 (1);
-  result += foo3 (1);
-  result += foo4 (1);
-  result += foo5 (1);
-  result += foo6 (1);
-  result += foo7 (1);
-  result += foo8 (1);
-  result += foo9 (1);
-  assert (result == 54);
-  return 0;
-}
+#include "zero-scratch-regs-10.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
index 73c3794..25891ac 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
@@ -1,15 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */
 
-volatile int result = 0;
-int 
-__attribute__((noipa))
-foo (int x)
-{
-  return x;
-}
-int main()
-{
-  result = foo (2);
-  return 0;
-}
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c
index d3db757..7c3d286 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c
@@ -1,15 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */
 
-volatile int result = 0;
-int 
-__attribute__((noipa))
-foo (int x)
-{
-  return x;
-}
-int main()
-{
-  result = foo (2);
-  return 0;
-}
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c
index 029aa79..ba28c06 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c
@@ -1,15 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */
 
-volatile int result = 0;
-int 
-__attribute__((noipa))
-foo (int x)
-{
-  return x;
-}
-int main()
-{
-  result = foo (2);
-  return 0;
-}
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c
index 509ce20..26679a4 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c
@@ -1,15 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=used" } */
 
-volatile int result = 0;
-int 
-__attribute__((noipa))
-foo (int x)
-{
-  return x;
-}
-int main()
-{
-  result = foo (2);
-  return 0;
-}
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c
index 56d8063..80f5bbb 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c
@@ -1,15 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */
 
-volatile int result = 0;
-int 
-__attribute__((noipa))
-foo (int x)
-{
-  return x;
-}
-int main()
-{
-  result = foo (2);
-  return 0;
-}
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c
index 40378ed..159f35c 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c
@@ -1,15 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */
 
-volatile int result = 0;
-int 
-__attribute__((noipa))
-foo (int x)
-{
-  return x;
-}
-int main()
-{
-  result = foo (2);
-  return 0;
-}
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c
index 7bdef95..c1faaf0 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c
@@ -1,15 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */
 
-volatile int result = 0;
-int 
-__attribute__((noipa))
-foo (int x)
-{
-  return x;
-}
-int main()
-{
-  result = foo (2);
-  return 0;
-}
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c
index 032da1a..3f14bac 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c
@@ -1,15 +1,4 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fzero-call-used-regs=all" } */
 
-volatile int result = 0;
-int 
-__attribute__((noipa))
-foo (int x)
-{
-  return x;
-}
-int main()
-{
-  result = foo (2);
-  return 0;
-}
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
index c60e946..1e75795 100644
--- a/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
@@ -1,10 +1,16 @@ 
-/* { dg-do compile} */
+/* { dg-do compile } */
 /* { dg-options "-O2" } */
 
 int result __attribute__ ((zero_call_used_regs("all"))); /* { dg-error "attribute applies only to functions" } */
 int
 __attribute__ ((zero_call_used_regs("gpr-arg-all")))
-foo1 (int x) /* { dg-error "unrecognized zero_call_used_regs attribute" } */
+foo1 (int x) /* { dg-error "unrecognized 'zero_call_used_regs' attribute argument" } */
 {
   return (x + 1);
 }
+int
+__attribute__ ((zero_call_used_regs(1)))
+foo2 (int x) /* { dg-error "argument not a string" } */
+{
+  return (x + 2);
+}
-- 
1.8.3.1


******The whole patch:

From bc91029c821ed8c3aad2023375e2ab44b0f33166 Mon Sep 17 00:00:00 2001
From: qing zhao <qing.zhao@oracle.com>
Date: Thu, 1 Oct 2020 00:27:57 +0000
Subject: [PATCH] The 6th version of -fzero-call-used-regs

We will provide a new feature into GCC:

Add -fzero-call-used-regs=[skip|used-gpr-arg|used-arg|all-gpr-arg
			   |all-arg|used-gpr|all-gpr|used|all]
command-line option
and
zero_call_used_regs("skip|used-gpr-arg|used-arg|all-gpr-arg
		     |all-arg|used-gpr|all-gpr|used|all")
function attribues:

1. -fzero-call-used-regs=skip and zero_call_used_regs("skip")
2. -fzero-call-used-regs=used-gpr-arg and zero_call_used_regs("used-gpr-arg")
3. -fzero-call-used-regs=used-arg and zero_call_used_regs("used-arg")
4. -fzero-call-used-regs=all-gpr-arg and zero_call_used_regs("all-gpr-arg")
5. -fzero-call-used-regs=all-arg and zero_call_used_regs("all-arg")
6. -fzero-call-used-regs=used-gpr and zero_call_used_regs("used-gpr")
7. -fzero-call-used-regs=all-gpr and zero_call_used_regs("all-gpr")
8. -fzero-call-used-regs=used and zero_call_used_regs("used")
9. -fzero-call-used-regs=all and zero_call_used_regs("all")
---
 gcc/c-family/c-attribs.c                           |  51 ++++
 gcc/common.opt                                     |   8 +
 gcc/config/i386/i386.c                             | 294 +++++++++++++++++++++
 gcc/df-scan.c                                      |  12 +-
 gcc/df.h                                           |   1 +
 gcc/doc/extend.texi                                |  86 ++++++
 gcc/doc/invoke.texi                                |  15 +-
 gcc/doc/tm.texi                                    |  12 +
 gcc/doc/tm.texi.in                                 |   2 +
 gcc/emit-rtl.h                                     |   3 +
 gcc/flag-types.h                                   |  22 ++
 gcc/function.c                                     | 190 ++++++++++++-
 gcc/optabs.c                                       |  42 +++
 gcc/optabs.h                                       |   2 +
 gcc/opts.c                                         |  45 ++++
 gcc/opts.h                                         |   6 +
 gcc/passes.def                                     |   1 +
 gcc/recog.c                                        |  18 +-
 gcc/recog.h                                        |   1 +
 gcc/resource.c                                     |   2 +-
 gcc/target.def                                     |  15 ++
 gcc/targhooks.c                                    |  32 +++
 gcc/targhooks.h                                    |   1 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-1.c   |  15 ++
 gcc/testsuite/c-c++-common/zero-scratch-regs-10.c  |  92 +++++++
 gcc/testsuite/c-c++-common/zero-scratch-regs-11.c  |   4 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-2.c   |   4 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-3.c   |   4 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-4.c   |   4 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-5.c   |   4 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-6.c   |   4 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-7.c   |   4 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-8.c   |   4 +
 gcc/testsuite/c-c++-common/zero-scratch-regs-9.c   |   4 +
 .../c-c++-common/zero-scratch-regs-attr-usages.c   |  16 ++
 .../gcc.target/i386/zero-scratch-regs-1.c          |  12 +
 .../gcc.target/i386/zero-scratch-regs-10.c         |  21 ++
 .../gcc.target/i386/zero-scratch-regs-11.c         |  39 +++
 .../gcc.target/i386/zero-scratch-regs-12.c         |  39 +++
 .../gcc.target/i386/zero-scratch-regs-13.c         |  21 ++
 .../gcc.target/i386/zero-scratch-regs-14.c         |  19 ++
 .../gcc.target/i386/zero-scratch-regs-15.c         |  14 +
 .../gcc.target/i386/zero-scratch-regs-16.c         |  14 +
 .../gcc.target/i386/zero-scratch-regs-17.c         |  13 +
 .../gcc.target/i386/zero-scratch-regs-18.c         |  13 +
 .../gcc.target/i386/zero-scratch-regs-19.c         |  12 +
 .../gcc.target/i386/zero-scratch-regs-2.c          |  19 ++
 .../gcc.target/i386/zero-scratch-regs-20.c         |  23 ++
 .../gcc.target/i386/zero-scratch-regs-21.c         |  14 +
 .../gcc.target/i386/zero-scratch-regs-22.c         |  21 ++
 .../gcc.target/i386/zero-scratch-regs-23.c         |  29 ++
 .../gcc.target/i386/zero-scratch-regs-24.c         |  10 +
 .../gcc.target/i386/zero-scratch-regs-25.c         |  10 +
 .../gcc.target/i386/zero-scratch-regs-26.c         |  23 ++
 .../gcc.target/i386/zero-scratch-regs-27.c         |  15 ++
 .../gcc.target/i386/zero-scratch-regs-28.c         |  16 ++
 .../gcc.target/i386/zero-scratch-regs-29.c         |  10 +
 .../gcc.target/i386/zero-scratch-regs-3.c          |  12 +
 .../gcc.target/i386/zero-scratch-regs-30.c         |  12 +
 .../gcc.target/i386/zero-scratch-regs-31.c         |  12 +
 .../gcc.target/i386/zero-scratch-regs-4.c          |  14 +
 .../gcc.target/i386/zero-scratch-regs-5.c          |  20 ++
 .../gcc.target/i386/zero-scratch-regs-6.c          |  14 +
 .../gcc.target/i386/zero-scratch-regs-7.c          |  13 +
 .../gcc.target/i386/zero-scratch-regs-8.c          |  19 ++
 .../gcc.target/i386/zero-scratch-regs-9.c          |  15 ++
 gcc/tree-pass.h                                    |   1 +
 67 files changed, 1552 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-1.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-10.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-3.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-4.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-5.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-6.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-7.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-8.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-9.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index c779d13..8da1dc7 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -138,6 +138,8 @@  static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *);
 static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
 static tree ignore_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *);
+static tree handle_zero_call_used_regs_attribute (tree *, tree, tree, int,
+						  bool *);
 static tree handle_argspec_attribute (tree *, tree, tree, int, bool *);
 static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
 static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *);
@@ -437,6 +439,8 @@  const struct attribute_spec c_common_attribute_table[] =
 			      ignore_attribute, NULL },
   { "no_split_stack",	      0, 0, true,  false, false, false,
 			      handle_no_split_stack_attribute, NULL },
+  { "zero_call_used_regs",    1, 1, true, false, false, false,
+			      handle_zero_call_used_regs_attribute, NULL },
   /* For internal use only (marking of function arguments).
      The name contains a space to prevent its usage in source code.  */
   { "arg spec",		      1, -1, true, false, false, false,
@@ -4959,6 +4963,53 @@  handle_no_split_stack_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle a "zero_call_used_regs" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_zero_call_used_regs_attribute (tree *node, tree name, tree args,
+				      int ARG_UNUSED (flags),
+				      bool *no_add_attrs)
+{
+  tree decl = *node;
+  tree id = TREE_VALUE (args);
+
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute applies only to functions", name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (id) != STRING_CST)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE argument not a string", name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  bool found = false;
+  for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
+    if (strcmp (TREE_STRING_POINTER (id),
+		zero_call_used_regs_opts[i].name) == 0)
+      {
+	found = true;
+	break;
+      }
+
+  if (!found)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"unrecognized %qE attribute argument %qs",
+		name, TREE_STRING_POINTER (id));
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 /* Handle a "returns_nonnull" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/common.opt b/gcc/common.opt
index 292c2de..d716ea1 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -228,6 +228,10 @@  unsigned int flag_sanitize_coverage
 Variable
 bool dump_base_name_prefixed = false
 
+; What subset of registers should be zeroed on function return
+Variable
+unsigned int flag_zero_call_used_regs
+
 ###
 Driver
 
@@ -3111,6 +3115,10 @@  fzero-initialized-in-bss
 Common Report Var(flag_zero_initialized_in_bss) Init(1)
 Put zero initialized data in the bss section.
 
+fzero-call-used-regs=
+Common Report RejectNegative Joined
+Clear call-used registers upon function return.
+
 g
 Common Driver RejectNegative JoinedOrMissing
 Generate debug information in default format.
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index f684954..4360595 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -3551,6 +3551,297 @@  ix86_function_value_regno_p (const unsigned int regno)
   return false;
 }
 
+/* Check whether the register REGNO should be zeroed on X86.
+   When ALL_SSE_ZEROED is true, all SSE registers have been zeroed
+   together, no need to zero it again.
+   When NEED_ZERO_MMX is true, MMX registers should be cleared.  */
+
+static bool
+zero_call_used_regno_p (const unsigned int regno,
+			bool all_sse_zeroed,
+			bool need_zero_mmx)
+{
+  return GENERAL_REGNO_P (regno)
+	 || (!all_sse_zeroed && SSE_REGNO_P (regno))
+	 || MASK_REGNO_P (regno)
+	 || (need_zero_mmx && MMX_REGNO_P (regno));
+}
+
+/* Return the machine_mode that is used to zero register REGNO.  */
+
+static machine_mode
+zero_call_used_regno_mode (const unsigned int regno)
+{
+  /* NB: We only need to zero the lower 32 bits for integer registers
+     and the lower 128 bits for vector registers since destination are
+     zero-extended to the full register width.  */
+  if (GENERAL_REGNO_P (regno))
+    return SImode;
+  else if (SSE_REGNO_P (regno))
+    return V4SFmode;
+  else if (MASK_REGNO_P (regno))
+    return HImode;
+  else if (MMX_REGNO_P (regno))
+    return V4HImode;
+  else
+    gcc_unreachable ();
+}
+
+/* Generate a rtx to zero all vector registers together if possible,
+   otherwise, return NULL.  */
+
+static rtx
+zero_all_vector_registers (HARD_REG_SET need_zeroed_hardregs)
+{
+  if (!TARGET_AVX)
+    return NULL;
+
+  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if ((IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG)
+	 || (TARGET_64BIT
+	     && (REX_SSE_REGNO_P (regno)
+		 || (TARGET_AVX512F && EXT_REX_SSE_REGNO_P (regno)))))
+	&& !TEST_HARD_REG_BIT (need_zeroed_hardregs, regno))
+      return NULL;
+
+  return gen_avx_vzeroall ();
+}
+
+/* Generate insns to zero all st registers together.
+   Return true when zeroing instructions are generated.
+   Assume the number of st registers that are zeroed is num_of_st,
+   we will emit the following sequence to zero them together:
+		  fldz;		\
+		  fldz;		\
+		  ...
+		  fldz;		\
+		  fstp %%st(0);	\
+		  fstp %%st(0);	\
+		  ...
+		  fstp %%st(0);
+   i.e., num_of_st fldz followed by num_of_st fstp to clear the stack
+   mark stack slots empty.
+
+   How to compute the num_of_st:
+   There is no direct mapping from stack registers to hard register
+   numbers.  If one stack register needs to be cleared, we don't know
+   where in the stack the value remains.  So, if any stack register
+   needs to be cleared, the whole stack should be cleared.  However,
+   x87 stack registers that hold the return value should be excluded.
+   x87 returns in the top (two for complex values) register, so
+   num_of_st should be 7/6 when x87 returns, otherwise it will be 8.  */
+
+
+static bool
+zero_all_st_registers (HARD_REG_SET need_zeroed_hardregs)
+{
+  unsigned int num_of_st = 0;
+  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if ((STACK_REGNO_P (regno) || MMX_REGNO_P (regno))
+	&& TEST_HARD_REG_BIT (need_zeroed_hardregs, regno))
+      {
+	num_of_st++;
+	break;
+      }
+
+  if (num_of_st == 0)
+    return false;
+
+  bool return_with_x87 = false;
+  return_with_x87 = (crtl->return_rtx
+		     && (STACK_REG_P (crtl->return_rtx)));
+
+  bool complex_return = false;
+  complex_return = (crtl->return_rtx
+		    && COMPLEX_MODE_P (GET_MODE (crtl->return_rtx)));
+
+  if (return_with_x87)
+    if (complex_return)
+      num_of_st = 6;
+    else
+      num_of_st = 7;
+  else
+    num_of_st = 8;
+
+  rtx st_reg = gen_rtx_REG (XFmode, FIRST_STACK_REG);
+  for (unsigned int i = 0; i < num_of_st; i++)
+    emit_insn (gen_rtx_SET (st_reg, CONST0_RTX (XFmode)));
+
+  for (unsigned int i = 0; i < num_of_st; i++)
+    {
+      rtx insn;
+      insn = emit_insn (gen_rtx_SET (st_reg, st_reg));
+      add_reg_note (insn, REG_DEAD, st_reg);
+    }
+  return true;
+}
+
+
+/* When the routine exit in MMX mode, if any ST register needs
+   to be zeroed, we should clear all MMX registers except the
+   RET_MMX_REGNO that holds the return value.  */
+static bool
+zero_all_mm_registers (HARD_REG_SET need_zeroed_hardregs,
+		       unsigned int ret_mmx_regno)
+{
+  bool need_zero_all_mm = false;
+  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (STACK_REGNO_P (regno)
+	&& TEST_HARD_REG_BIT (need_zeroed_hardregs, regno))
+      {
+	need_zero_all_mm = true;
+	break;
+      }
+
+  if (!need_zero_all_mm)
+    return false;
+
+  rtx zero_mmx = NULL_RTX;
+  machine_mode mode = V4HImode;
+  for (unsigned int regno = FIRST_MMX_REG; regno <= LAST_MMX_REG; regno++)
+    if (regno != ret_mmx_regno)
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	if (zero_mmx == NULL_RTX)
+	  {
+	    zero_mmx = reg;
+	    emit_insn (gen_rtx_SET (reg, CONST0_RTX (mode)));
+	  }
+	else
+	  emit_move_insn (reg, zero_mmx);
+      }
+  return true;
+}
+
+/* TARGET_ZERO_CALL_USED_REGS.  */
+/* Generate a sequence of instructions that zero registers specified by
+   NEED_ZEROED_HARDREGS.  Return the ZEROED_HARDREGS that are actually
+   zeroed.  */
+static HARD_REG_SET
+ix86_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs)
+{
+  HARD_REG_SET zeroed_hardregs;
+  bool all_sse_zeroed = false;
+  bool all_st_zeroed = false;
+  bool all_mm_zeroed = false;
+
+  CLEAR_HARD_REG_SET (zeroed_hardregs);
+
+  /* first, let's see whether we can zero all vector registers together.  */
+  rtx zero_all_vec_insn = zero_all_vector_registers (need_zeroed_hardregs);
+  if (zero_all_vec_insn)
+    {
+      emit_insn (zero_all_vec_insn);
+      all_sse_zeroed = true;
+    }
+
+  /* mm/st registers are shared registers set, we should follow the following
+     rules to clear them:
+			MMX exit mode	      x87 exit mode
+	-------------|----------------------|---------------
+	uses x87 reg | clear all MMX	    | clear all x87
+	uses MMX reg | clear individual MMX | clear all x87
+	x87 + MMX    | clear all MMX	    | clear all x87
+
+     first, we should decide which mode (MMX mode or x87 mode) the function
+     exit with.  */
+
+  bool exit_with_mmx_mode = (crtl->return_rtx
+			     && (MMX_REG_P (crtl->return_rtx)));
+
+  if (!exit_with_mmx_mode)
+    /* x87 exit mode, we should zero all st registers together.  */
+    {
+      all_st_zeroed = zero_all_st_registers (need_zeroed_hardregs);
+      if (all_st_zeroed)
+	SET_HARD_REG_BIT (zeroed_hardregs, FIRST_STACK_REG);
+    }
+  else
+    /* MMX exit mode, check whether we can zero all mm registers.  */
+    {
+      unsigned int exit_mmx_regno = REGNO (crtl->return_rtx);
+      all_mm_zeroed = zero_all_mm_registers (need_zeroed_hardregs,
+					     exit_mmx_regno);
+      if (all_mm_zeroed)
+	for (unsigned int regno = FIRST_MMX_REG; regno <= LAST_MMX_REG; regno++)
+	  if (regno != exit_mmx_regno)
+	    SET_HARD_REG_BIT (zeroed_hardregs, regno);
+    }
+
+  /* Now, generate instructions to zero all the other registers.  */
+
+  rtx zero_gpr = NULL_RTX;
+  rtx zero_vector = NULL_RTX;
+  rtx zero_mask = NULL_RTX;
+  rtx zero_mmx = NULL_RTX;
+
+  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    {
+      if (!TEST_HARD_REG_BIT (need_zeroed_hardregs, regno))
+	continue;
+      if (!zero_call_used_regno_p (regno, all_sse_zeroed,
+				   exit_with_mmx_mode && !all_mm_zeroed))
+	continue;
+
+      SET_HARD_REG_BIT (zeroed_hardregs, regno);
+
+      rtx reg, tmp, zero_rtx;
+      machine_mode mode = zero_call_used_regno_mode (regno);
+
+      reg = gen_rtx_REG (mode, regno);
+      zero_rtx = CONST0_RTX (mode);
+
+      if (mode == SImode)
+	if (zero_gpr == NULL_RTX)
+	  {
+	    zero_gpr = reg;
+	    tmp = gen_rtx_SET (reg, zero_rtx);
+	    if (!TARGET_USE_MOV0 || optimize_insn_for_size_p ())
+	      {
+		rtx clob = gen_rtx_CLOBBER (VOIDmode,
+					    gen_rtx_REG (CCmode,
+							 FLAGS_REG));
+		tmp = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2,
+							     tmp,
+							     clob));
+	      }
+	    emit_insn (tmp);
+	  }
+	else
+	  emit_move_insn (reg, zero_gpr);
+      else if (mode == V4SFmode)
+	if (zero_vector == NULL_RTX)
+	  {
+	    zero_vector = reg;
+	    tmp = gen_rtx_SET (reg, zero_rtx);
+	    emit_insn (tmp);
+	  }
+	else
+	  emit_move_insn (reg, zero_vector);
+      else if (mode == HImode)
+	if (zero_mask == NULL_RTX)
+	  {
+	    zero_mask = reg;
+	    tmp = gen_rtx_SET (reg, zero_rtx);
+	    emit_insn (tmp);
+	  }
+	else
+	  emit_move_insn (reg, zero_mask);
+      else if (mode == V4HImode)
+	if (zero_mmx == NULL_RTX)
+	  {
+	    zero_mmx = reg;
+	    tmp = gen_rtx_SET (reg, zero_rtx);
+	    emit_insn (tmp);
+	  }
+	else
+	  emit_move_insn (reg, zero_mmx);
+      else
+	gcc_unreachable ();
+    }
+  return zeroed_hardregs;
+}
+
 /* Define how to find the value returned by a function.
    VALTYPE is the data type of the value (as a tree).
    If the precise function being called is known, FUNC is its FUNCTION_DECL;
@@ -23229,6 +23520,9 @@  ix86_run_selftests (void)
 #undef TARGET_FUNCTION_VALUE_REGNO_P
 #define TARGET_FUNCTION_VALUE_REGNO_P ix86_function_value_regno_p
 
+#undef TARGET_ZERO_CALL_USED_REGS
+#define TARGET_ZERO_CALL_USED_REGS ix86_zero_call_used_regs
+
 #undef TARGET_PROMOTE_FUNCTION_MODE
 #define TARGET_PROMOTE_FUNCTION_MODE ix86_promote_function_mode
 
diff --git a/gcc/df-scan.c b/gcc/df-scan.c
index 93b060f..9e75c13 100644
--- a/gcc/df-scan.c
+++ b/gcc/df-scan.c
@@ -3614,6 +3614,14 @@  df_update_entry_block_defs (void)
 }
 
 
+/* Return true if REGNO is used by the epilogue.  */
+bool
+df_epilogue_uses_p (unsigned int regno)
+{
+  return (EPILOGUE_USES (regno)
+	  || TEST_HARD_REG_BIT (crtl->must_be_zero_on_return, regno));
+}
+
 /* Set the bit for regs that are considered being used at the exit. */
 
 static void
@@ -3661,7 +3669,7 @@  df_get_exit_block_use_set (bitmap exit_block_uses)
      epilogue as being live at the end of the function since they
      may be referenced by our caller.  */
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    if (global_regs[i] || EPILOGUE_USES (i))
+    if (global_regs[i] || df_epilogue_uses_p (i))
       bitmap_set_bit (exit_block_uses, i);
 
   if (targetm.have_epilogue () && epilogue_completed)
@@ -3802,7 +3810,6 @@  df_hard_reg_init (void)
   initialized = true;
 }
 
-
 /* Recompute the parts of scanning that are based on regs_ever_live
    because something changed in that array.  */
 
@@ -3862,7 +3869,6 @@  df_regs_ever_live_p (unsigned int regno)
   return regs_ever_live[regno];
 }
 
-
 /* Set regs_ever_live[REGNO] to VALUE.  If this cause regs_ever_live
    to change, schedule that change for the next update.  */
 
diff --git a/gcc/df.h b/gcc/df.h
index 8b6ca8c..0f098d7 100644
--- a/gcc/df.h
+++ b/gcc/df.h
@@ -1085,6 +1085,7 @@  extern void df_update_entry_exit_and_calls (void);
 extern bool df_hard_reg_used_p (unsigned int);
 extern unsigned int df_hard_reg_used_count (unsigned int);
 extern bool df_regs_ever_live_p (unsigned int);
+extern bool df_epilogue_uses_p (unsigned int);
 extern void df_set_regs_ever_live (unsigned int, bool);
 extern void df_compute_regs_ever_live (bool);
 extern void df_scan_verify (void);
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index c9f7299..25b3909 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3992,6 +3992,92 @@  performing a link with relocatable output (i.e.@: @code{ld -r}) on them.
 A declaration to which @code{weakref} is attached and that is associated
 with a named @code{target} must be @code{static}.
 
+@item zero_call_used_regs ("@var{choice}")
+@cindex @code{zero_call_used_regs} function attribute
+
+The @code{zero_call_used_regs} attribute causes the compiler to zero
+a subset of all call-used registers@footnote{A ``call-used'' register
+is a register whose contents can be changed by a function call;
+therefore, a caller cannot assume that the register has the same contents
+on return from the function as it had before calling the function.  Such
+registers are also called ``call-clobbered'', ``caller-saved'', or
+``volatile''.} at function return.
+This is used to increase program security by either mitigating
+Return-Oriented Programming (ROP) attacks or preventing information leakage
+through registers.
+
+In order to satisfy users with different security needs and control the
+run-time overhead at the same time, @var{choice} parameter provides a
+flexible way to choose the subset of the call-used registers to be zeroed.
+The three basic values of @var{choice} are:
+
+@itemize @bullet
+@item
+@samp{skip} doesn't zero any call-used registers.
+
+@item
+@samp{used} only zeros call-used registers that are used in the function.
+A ``used'' register is one whose content has been set or referenced in
+the function.
+
+@item
+@samp{all} zeros all call-used registers.
+@end itemize
+
+In addition to these three basic choices, it is possible to modify
+@samp{used} or @samp{all} as follows:
+
+@itemize @bullet
+@item
+Adding @samp{-gpr} restricts the zeroing to general-purpose registers.
+
+@item
+Adding @samp{-arg} restricts the zeroing to registers that can sometimes
+be used to pass function arguments.  This includes all argument registers
+defined by the platform's calling conversion, regardless of whether the
+function uses those registers for function arguments or not.
+@end itemize
+
+The modifiers can be used individually or together.  If they are used
+together, they must appear in the order above.
+
+The full list of @var{choice}s is therefore:
+
+@table @code
+@item skip
+doesn't zero any call-used register.
+
+@item used
+only zeros call-used registers that are used in the function.
+
+@item used-gpr
+only zeros call-used general purpose registers that are used in the function.
+
+@item used-arg
+only zeros call-used registers that are used in the function and pass arguments.
+
+@item used-gpr-arg
+only zeros call-used general purpose registers that are used in the function
+and pass arguments.
+
+@item all
+zeros all call-used registers.
+
+@item all-gpr
+zeros all call-used general purpose registers.
+
+@item all-arg
+zeros all call-used registers that pass arguments.
+
+@item all-gpr-arg
+zeros all call-used general purpose registers that pass
+arguments.
+@end table
+
+Of this list, @samp{used-arg}, @samp{used-gpr-arg}, @samp{all-arg},
+and @samp{all-gpr-arg} are mainly used for ROP mitigation.
+
+The default for the attribute is controlled by @option{-fzero-call-used-regs}.
 @end table
 
 @c This is the end of the target-independent attribute table
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index c049932..23834a2 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -550,7 +550,7 @@  Objective-C and Objective-C++ Dialects}.
 -funit-at-a-time  -funroll-all-loops  -funroll-loops @gol
 -funsafe-math-optimizations  -funswitch-loops @gol
 -fipa-ra  -fvariable-expansion-in-unroller  -fvect-cost-model  -fvpt @gol
--fweb  -fwhole-program  -fwpa  -fuse-linker-plugin @gol
+-fweb  -fwhole-program  -fwpa  -fuse-linker-plugin -fzero-call-used-regs @gol
 --param @var{name}=@var{value}
 -O  -O0  -O1  -O2  -O3  -Os  -Ofast  -Og}
 
@@ -12550,6 +12550,19 @@  int foo (void)
 
 Not all targets support this option.
 
+@item -fzero-call-used-regs=@var{choice}
+@opindex fzero-call-used-regs
+Zero call-used registers at function return to increase program
+security by either mitigating Return-Oriented Programming (ROP)
+attacks or preventing information leakage through registers.
+
+The possible values of @var{choice} are the same as for the
+@code{zero_call_used_regs} attribute (@pxref{Function Attributes}).
+The default is @samp{skip}.
+
+You can control this behavior for a specific function by using the function
+attribute @code{zero_call_used_regs} (@pxref{Function Attributes}).
+
 @item --param @var{name}=@var{value}
 @opindex param
 In some places, GCC uses various constants to control the amount of
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 97437e8..18f8ad9 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -12053,6 +12053,18 @@  argument list due to stack realignment.  Return @code{NULL} if no DRAP
 is needed.
 @end deftypefn
 
+@deftypefn {Target Hook} HARD_REG_SET TARGET_ZERO_CALL_USED_REGS (HARD_REG_SET @var{selected_regs})
+This target hook emits instructions to zero the subset of @var{selected_regs}
+that could conceivably contain values that are useful to an attacker.
+Return the set of registers that were actually cleared.
+
+The default implementation uses normal move instructions to zero
+all the registers in @var{selected_regs}.  Define this hook if the
+target has more efficient ways of zeroing certain registers,
+or if you believe that certain registers would never contain
+values that are useful to an attacker.
+@end deftypefn
+
 @deftypefn {Target Hook} bool TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS (void)
 When optimization is disabled, this hook indicates whether or not
 arguments should be allocated to stack slots.  Normally, GCC allocates
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 412e22c..a67dbea 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8111,6 +8111,8 @@  and the associated definitions of those functions.
 
 @hook TARGET_GET_DRAP_RTX
 
+@hook TARGET_ZERO_CALL_USED_REGS
+
 @hook TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
 
 @hook TARGET_CONST_ANCHOR
diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h
index 92ad0dd6..4658c4a 100644
--- a/gcc/emit-rtl.h
+++ b/gcc/emit-rtl.h
@@ -310,6 +310,9 @@  struct GTY(()) rtl_data {
      sets them.  */
   HARD_REG_SET asm_clobbers;
 
+  /* All hard registers that need to be zeroed at the return of the routine.  */
+  HARD_REG_SET must_be_zero_on_return;
+
   /* The highest address seen during shorten_branches.  */
   int max_insn_address;
 };
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 852ea76..51c7e5f 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -20,6 +20,8 @@  along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_FLAG_TYPES_H
 #define GCC_FLAG_TYPES_H
 
+#if !defined(IN_LIBGCC2) && !defined(IN_TARGET_LIBS) && !defined(IN_RTS)
+
 enum debug_info_type
 {
   NO_DEBUG,	    /* Write no debug info.  */
@@ -285,6 +287,24 @@  enum sanitize_code {
 				  | SANITIZE_BOUNDS_STRICT
 };
 
+/* Different settings for zeroing subset of registers.  */
+namespace zero_regs_flag {
+  const unsigned int UNSET = 0;
+  const unsigned int SKIP = 1UL << 0;
+  const unsigned int ONLY_USED = 1UL << 1;
+  const unsigned int ONLY_GPR = 1UL << 2;
+  const unsigned int ONLY_ARG = 1UL << 3;
+  const unsigned int ENABLED = 1UL << 4;
+  const unsigned int USED_GPR_ARG = ENABLED | ONLY_USED | ONLY_GPR | ONLY_ARG;
+  const unsigned int USED_GPR = ENABLED | ONLY_USED | ONLY_GPR;
+  const unsigned int USED_ARG = ENABLED | ONLY_USED | ONLY_ARG;
+  const unsigned int USED = ENABLED | ONLY_USED;
+  const unsigned int ALL_GPR_ARG = ENABLED | ONLY_GPR | ONLY_ARG;
+  const unsigned int ALL_GPR = ENABLED | ONLY_GPR;
+  const unsigned int ALL_ARG = ENABLED | ONLY_ARG;
+  const unsigned int ALL = ENABLED;
+}
+
 /* Settings of flag_incremental_link.  */
 enum incremental_link {
   INCREMENTAL_LINK_NONE,
@@ -382,4 +402,6 @@  enum parloops_schedule_type
   PARLOOPS_SCHEDULE_RUNTIME
 };
 
+#endif
+
 #endif /* ! GCC_FLAG_TYPES_H */
diff --git a/gcc/function.c b/gcc/function.c
index c612959..76a5037 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -46,10 +46,12 @@  along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "expmed.h"
 #include "optabs.h"
+#include "opts.h"
 #include "regs.h"
 #include "emit-rtl.h"
 #include "recog.h"
 #include "rtl-error.h"
+#include "hard-reg-set.h"
 #include "alias.h"
 #include "fold-const.h"
 #include "stor-layout.h"
@@ -5815,6 +5817,103 @@  make_prologue_seq (void)
   return seq;
 }
 
+/* Emit a sequence of insns to zero the call-used registers before RET
+   according to ZERO_REGS_TYPE.  */
+
+static void
+gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type)
+{
+  bool only_gpr = true;
+  bool only_used = true;
+  bool only_arg = true;
+
+  /* No need to zero call-used-regs in main ().  */
+  if (MAIN_NAME_P (DECL_NAME (current_function_decl)))
+    return;
+
+  /* No need to zero call-used-regs if __builtin_eh_return is called
+     since it isn't a normal function return.  */
+  if (crtl->calls_eh_return)
+    return;
+
+  /* If only_gpr is true, only zero call-used registers that are
+     general-purpose registers; if only_used is true, only zero
+     call-used registers that are used in the current function;
+     if only_arg is true, only zero call-used registers that pass
+     parameters defined by the flatform's calling conversion.  */
+
+  using namespace zero_regs_flag;
+
+  only_gpr = zero_regs_type & ONLY_GPR;
+  only_used = zero_regs_type & ONLY_USED;
+  only_arg = zero_regs_type & ONLY_ARG;
+
+  /* For each of the hard registers, we should zero it if:
+	    1. it is a call-used register;
+	and 2. it is not a fixed register;
+	and 3. it is not live at the return of the routine;
+	and 4. it is general registor if only_gpr is true;
+	and 5. it is used in the routine if only_used is true;
+	and 6. it is a register that passes parameter if only_arg is true.  */
+
+  /* First, prepare the data flow information.  */
+  basic_block bb = BLOCK_FOR_INSN (ret);
+  auto_bitmap live_out;
+  bitmap_copy (live_out, df_get_live_out (bb));
+  df_simulate_initialize_backwards (bb, live_out);
+  df_simulate_one_insn_backwards (bb, ret, live_out);
+
+  HARD_REG_SET selected_hardregs;
+  CLEAR_HARD_REG_SET (selected_hardregs);
+  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    {
+      if (!crtl->abi->clobbers_full_reg_p (regno))
+	continue;
+      if (fixed_regs[regno])
+	continue;
+      if (REGNO_REG_SET_P (live_out, regno))
+	continue;
+      if (only_gpr
+	  && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
+	continue;
+      if (only_used && !df_regs_ever_live_p (regno))
+	continue;
+      if (only_arg && !FUNCTION_ARG_REGNO_P (regno))
+	continue;
+
+      /* Now this is a register that we might want to zero.  */
+      SET_HARD_REG_BIT (selected_hardregs, regno);
+    }
+
+  if (hard_reg_set_empty_p (selected_hardregs))
+    return;
+
+  /* Now that we have a hard register set that needs to be zeroed, pass it to
+     target to generate zeroing sequence.  */
+  HARD_REG_SET zeroed_hardregs;
+  start_sequence ();
+  zeroed_hardregs = targetm.calls.zero_call_used_regs (selected_hardregs);
+  rtx_insn *seq = get_insns ();
+  end_sequence ();
+  if (seq)
+    {
+      /* Emit the memory blockage and register clobber asm volatile before
+	 the whole sequence.  */
+      start_sequence ();
+      expand_asm_reg_clobber_mem_blockage (zeroed_hardregs);
+      rtx_insn *seq_barrier = get_insns ();
+      end_sequence ();
+
+      emit_insn_before (seq_barrier, ret);
+      emit_insn_before (seq, ret);
+
+      /* Update the data flow information.  */
+      crtl->must_be_zero_on_return |= zeroed_hardregs;
+      df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun));
+    }
+}
+
+
 /* Return a sequence to be used as the epilogue for the current function,
    or NULL.  */
 
@@ -6486,7 +6585,96 @@  make_pass_thread_prologue_and_epilogue (gcc::context *ctxt)
 {
   return new pass_thread_prologue_and_epilogue (ctxt);
 }
-

+
+namespace {
+
+const pass_data pass_data_zero_call_used_regs =
+{
+  RTL_PASS, /* type */
+  "zero_call_used_regs", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_zero_call_used_regs: public rtl_opt_pass
+{
+public:
+  pass_zero_call_used_regs (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_zero_call_used_regs, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual unsigned int execute (function *);
+
+}; // class pass_zero_call_used_regs
+
+unsigned int
+pass_zero_call_used_regs::execute (function *fun)
+{
+  using namespace zero_regs_flag;
+  unsigned int zero_regs_type = UNSET;
+
+  tree attr_zero_regs = lookup_attribute ("zero_call_used_regs",
+					  DECL_ATTRIBUTES (fun->decl));
+
+  /* Get the type of zero_call_used_regs from function attribute.
+     We have filtered out invalid attribute values already at this point.  */
+  if (attr_zero_regs)
+    {
+      /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
+	 is the attribute argument's value.  */
+      attr_zero_regs = TREE_VALUE (attr_zero_regs);
+      gcc_assert (TREE_CODE (attr_zero_regs) == TREE_LIST);
+      attr_zero_regs = TREE_VALUE (attr_zero_regs);
+      gcc_assert (TREE_CODE (attr_zero_regs) == STRING_CST);
+
+      for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
+	if (strcmp (TREE_STRING_POINTER (attr_zero_regs),
+		     zero_call_used_regs_opts[i].name) == 0)
+	  {
+	    zero_regs_type = zero_call_used_regs_opts[i].flag;
+ 	    break;
+	  }
+    }
+
+  if (!zero_regs_type)
+    zero_regs_type = flag_zero_call_used_regs;
+
+  /* No need to zero call-used-regs when no user request is present.  */
+  if (!(zero_regs_type & ENABLED))
+    return 0;
+
+  edge_iterator ei;
+  edge e;
+
+  /* This pass needs data flow information.  */
+  df_analyze ();
+
+  /* Iterate over the function's return instructions and insert any
+     register zeroing required by the -fzero-call-used-regs command-line
+     option or the "zero_call_used_regs" function attribute.  */
+  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
+    {
+      rtx_insn *insn = BB_END (e->src);
+      if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn)))
+	gen_call_used_regs_seq (insn, zero_regs_type);
+    }
+
+  return 0;
+}
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_zero_call_used_regs (gcc::context *ctxt)
+{
+  return new pass_zero_call_used_regs (ctxt);
+}
 
 /* If CONSTRAINT is a matching constraint, then return its number.
    Otherwise, return -1.  */
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 8ad7f4b..1820b91 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -6484,6 +6484,48 @@  expand_memory_blockage (void)
     expand_asm_memory_blockage ();
 }
 
+/* Generate asm volatile("" : : : "memory") as a memory blockage, at the
+   same time clobbering the register set specified by REGS.  */
+
+void
+expand_asm_reg_clobber_mem_blockage (HARD_REG_SET regs)
+{
+  rtx asm_op, clob_mem;
+
+  unsigned int num_of_regs = 0;
+  for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (TEST_HARD_REG_BIT (regs, i))
+      num_of_regs++;
+
+  asm_op = gen_rtx_ASM_OPERANDS (VOIDmode, "", "", 0,
+				 rtvec_alloc (0), rtvec_alloc (0),
+				 rtvec_alloc (0), UNKNOWN_LOCATION);
+  MEM_VOLATILE_P (asm_op) = 1;
+
+  rtvec v = rtvec_alloc (num_of_regs + 2);
+
+  clob_mem = gen_rtx_SCRATCH (VOIDmode);
+  clob_mem = gen_rtx_MEM (BLKmode, clob_mem);
+  clob_mem = gen_rtx_CLOBBER (VOIDmode, clob_mem);
+
+  RTVEC_ELT (v, 0) = asm_op;
+  RTVEC_ELT (v, 1) = clob_mem;
+
+  if (num_of_regs > 0)
+    {
+      unsigned int j = 2;
+      for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (TEST_HARD_REG_BIT (regs, i))
+	  {
+	    RTVEC_ELT (v, j) = gen_rtx_CLOBBER (VOIDmode, regno_reg_rtx[i]);
+ 	    j++;
+	  }
+      gcc_assert (j == (num_of_regs + 2));
+    }
+
+  emit_insn (gen_rtx_PARALLEL (VOIDmode, v));
+}
+
 /* This routine will either emit the mem_thread_fence pattern or issue a 
    sync_synchronize to generate a fence for memory model MEMMODEL.  */
 
diff --git a/gcc/optabs.h b/gcc/optabs.h
index 0b14700..bfa10c8 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -345,6 +345,8 @@  rtx expand_atomic_store (rtx, rtx, enum memmodel, bool);
 rtx expand_atomic_fetch_op (rtx, rtx, rtx, enum rtx_code, enum memmodel, 
 			      bool);
 
+extern void expand_asm_reg_clobber_mem_blockage (HARD_REG_SET);
+
 extern bool insn_operand_matches (enum insn_code icode, unsigned int opno,
 				  rtx operand);
 extern bool valid_multiword_target_p (rtx);
diff --git a/gcc/opts.c b/gcc/opts.c
index 3bda59a..8c439e4 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1776,6 +1776,25 @@  const struct sanitizer_opts_s coverage_sanitizer_opts[] =
   { NULL, 0U, 0UL, false }
 };
 
+using namespace zero_regs_flag;
+/* -fzero-call-used-regs= suboptions.  */
+const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
+{
+#define ZERO_CALL_USED_REGS_OPT(name, flags) \
+    { #name, flags }
+  ZERO_CALL_USED_REGS_OPT (skip, SKIP),
+  ZERO_CALL_USED_REGS_OPT (used-gpr-arg, USED_GPR_ARG),
+  ZERO_CALL_USED_REGS_OPT (used-gpr, USED_GPR),
+  ZERO_CALL_USED_REGS_OPT (used-arg, USED_ARG),
+  ZERO_CALL_USED_REGS_OPT (used, USED),
+  ZERO_CALL_USED_REGS_OPT (all-gpr-arg, ALL_GPR_ARG),
+  ZERO_CALL_USED_REGS_OPT (all-gpr, ALL_GPR),
+  ZERO_CALL_USED_REGS_OPT (all-arg, ALL_ARG),
+  ZERO_CALL_USED_REGS_OPT (all, ALL),
+#undef ZERO_CALL_USED_REGS_OPT
+  {NULL, 0U}
+};
+
 /* A struct for describing a run of chars within a string.  */
 
 class string_fragment
@@ -1970,6 +1989,27 @@  parse_no_sanitize_attribute (char *value)
   return flags;
 }
 
+/* Parse -fzero-call-used-regs suboptions from ARG, return the FLAGS.  */
+
+unsigned int
+parse_zero_call_used_regs_options (const char *arg)
+{
+  unsigned int flags = 0;
+
+  /* Check to see if the string matches a sub-option name.  */
+  for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
+    if (strcmp (arg, zero_call_used_regs_opts[i].name) == 0)
+      {
+	flags = zero_call_used_regs_opts[i].flag;
+	break;
+      }
+
+  if (!flags)
+    error ("unrecognized argument to %<-fzero-call-used-regs=%>: %qs", arg);
+
+  return flags;
+}
+
 /* Parse -falign-NAME format for a FLAG value.  Return individual
    parsed integer values into RESULT_VALUES array.  If REPORT_ERROR is
    set, print error message at LOC location.  */
@@ -2601,6 +2641,11 @@  common_handle_option (struct gcc_options *opts,
       /* Automatically sets -ftree-loop-vectorize and
 	 -ftree-slp-vectorize.  Nothing more to do here.  */
       break;
+    case OPT_fzero_call_used_regs_:
+      opts->x_flag_zero_call_used_regs
+	= parse_zero_call_used_regs_options (arg);
+      break;
+
     case OPT_fshow_column:
       dc->show_column = value;
       break;
diff --git a/gcc/opts.h b/gcc/opts.h
index 8f594b4..7d1e126 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -444,6 +444,12 @@  extern const struct sanitizer_opts_s
   bool can_recover;
 } sanitizer_opts[];
 
+extern const struct zero_call_used_regs_opts_s
+{
+  const char *const name;
+  unsigned int flag;
+} zero_call_used_regs_opts[];
+
 extern vec<const char *> help_option_arguments;
 
 extern void add_misspelling_candidates (auto_vec<char *> *candidates,
diff --git a/gcc/passes.def b/gcc/passes.def
index f865bdc..77d4676 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -492,6 +492,7 @@  along with GCC; see the file COPYING3.  If not see
       POP_INSERT_PASSES ()
       NEXT_PASS (pass_late_compilation);
       PUSH_INSERT_PASSES_WITHIN (pass_late_compilation)
+	  NEXT_PASS (pass_zero_call_used_regs);
 	  NEXT_PASS (pass_compute_alignments);
 	  NEXT_PASS (pass_variable_tracking);
 	  NEXT_PASS (pass_free_cfg);
diff --git a/gcc/recog.c b/gcc/recog.c
index ce83b7f..b458746 100644
--- a/gcc/recog.c
+++ b/gcc/recog.c
@@ -922,7 +922,23 @@  validate_simplify_insn (rtx_insn *insn)
       }
   return ((num_changes_pending () > 0) && (apply_change_group () > 0));
 }
-

+
+/* Check whether INSN matches a specific alternative of an .md pattern.  */
+
+bool
+valid_insn_p (rtx_insn *insn)
+{
+  recog_memoized (insn);
+  if (INSN_CODE (insn) < 0)
+    return false;
+  extract_insn (insn);
+  /* We don't know whether the insn will be in code that is optimized
+     for size or speed, so consider all enabled alternatives.  */
+  if (!constrain_operands (1, get_enabled_alternatives (insn)))
+    return false;
+  return true;
+}
+
 /* Return 1 if OP is a valid general operand for machine mode MODE.
    This is either a register reference, a memory reference,
    or a constant.  In the case of a memory reference, the address
diff --git a/gcc/recog.h b/gcc/recog.h
index ae3675f..d87456c 100644
--- a/gcc/recog.h
+++ b/gcc/recog.h
@@ -113,6 +113,7 @@  extern void validate_replace_src_group (rtx, rtx, rtx_insn *);
 extern bool validate_simplify_insn (rtx_insn *insn);
 extern int num_changes_pending (void);
 extern bool reg_fits_class_p (const_rtx, reg_class_t, int, machine_mode);
+extern bool valid_insn_p (rtx_insn *);
 
 extern int offsettable_memref_p (rtx);
 extern int offsettable_nonstrict_memref_p (rtx);
diff --git a/gcc/resource.c b/gcc/resource.c
index 0a9d594..90cf091 100644
--- a/gcc/resource.c
+++ b/gcc/resource.c
@@ -1186,7 +1186,7 @@  init_resource_info (rtx_insn *epilogue_insn)
 			       &end_of_function_needs, true);
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    if (global_regs[i] || EPILOGUE_USES (i))
+    if (global_regs[i] || df_epilogue_uses_p (i))
       SET_HARD_REG_BIT (end_of_function_needs.regs, i);
 
   /* The registers required to be live at the end of the function are
diff --git a/gcc/target.def b/gcc/target.def
index ed2da15..b916635 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -5080,6 +5080,21 @@  argument list due to stack realignment.  Return @code{NULL} if no DRAP\n\
 is needed.",
  rtx, (void), NULL)
 
+/* Generate instruction sequence to zero call used registers.  */
+DEFHOOK
+(zero_call_used_regs,
+ "This target hook emits instructions to zero the subset of @var{selected_regs}\n\
+that could conceivably contain values that are useful to an attacker.\n\
+Return the set of registers that were actually cleared.\n\
+\n\
+The default implementation uses normal move instructions to zero\n\
+all the registers in @var{selected_regs}.  Define this hook if the\n\
+target has more efficient ways of zeroing certain registers,\n\
+or if you believe that certain registers would never contain\n\
+values that are useful to an attacker.",
+ HARD_REG_SET, (HARD_REG_SET selected_regs),
+default_zero_call_used_regs)
+
 /* Return true if all function parameters should be spilled to the
    stack.  */
 DEFHOOK
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 5d94fce..4e4d100 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -56,6 +56,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa-alias.h"
 #include "gimple-expr.h"
 #include "memmodel.h"
+#include "backend.h"
+#include "emit-rtl.h"
+#include "df.h"
 #include "tm_p.h"
 #include "stringpool.h"
 #include "tree-vrp.h"
@@ -987,6 +990,35 @@  default_function_value_regno_p (const unsigned int regno ATTRIBUTE_UNUSED)
 #endif
 }
 
+/* The default hook for TARGET_ZERO_CALL_USED_REGS.  */
+
+HARD_REG_SET
+default_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs)
+{
+  gcc_assert (!hard_reg_set_empty_p (need_zeroed_hardregs));
+
+  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (TEST_HARD_REG_BIT (need_zeroed_hardregs, regno))
+      {
+	rtx_insn *last_insn = get_last_insn ();
+	machine_mode mode = GET_MODE (regno_reg_rtx[regno]);
+	rtx zero = CONST0_RTX (mode);
+	rtx_insn *insn = emit_move_insn (regno_reg_rtx[regno], zero);
+	if (!valid_insn_p (insn))
+	  {
+	    static bool issued_error;
+	    if (!issued_error)
+	      {
+		issued_error = true;
+		sorry ("%qs not supported on this target",
+			"-fzero-call-used_regs");
+	      }
+	    delete_insns_since (last_insn);
+	  }
+      }
+  return need_zeroed_hardregs;
+}
+
 rtx
 default_internal_arg_pointer (void)
 {
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 44ab926..e0a925f 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -160,6 +160,7 @@  extern unsigned int default_function_arg_round_boundary (machine_mode,
 							 const_tree);
 extern bool hook_bool_const_rtx_commutative_p (const_rtx, int);
 extern rtx default_function_value (const_tree, const_tree, bool);
+extern HARD_REG_SET default_zero_call_used_regs (HARD_REG_SET);
 extern rtx default_libcall_value (machine_mode, const_rtx);
 extern bool default_function_value_regno_p (const unsigned int);
 extern rtx default_internal_arg_pointer (void);
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c
new file mode 100644
index 0000000..2463353
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c
@@ -0,0 +1,15 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=skip" } */
+
+volatile int result = 0;
+int 
+__attribute__((noipa))
+foo (int x)
+{
+  return x;
+}
+int main()
+{
+  result = foo (2);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c
new file mode 100644
index 0000000..bdaf8e7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c
@@ -0,0 +1,92 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include <assert.h>
+int result = 0;
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("skip")))
+foo1 (int x)
+{
+  return (x + 1);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("used-gpr-arg")))
+foo2 (int x)
+{
+  return (x + 2);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("used-gpr")))
+foo3 (int x)
+{
+  return (x + 3);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("used-arg")))
+foo4 (int x)
+{
+  return (x + 4);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("used")))
+foo5 (int x)
+{
+  return (x + 5);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("all-gpr-arg")))
+foo6 (int x)
+{
+  return (x + 6);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("all-gpr")))
+foo7 (int x)
+{
+  return (x + 7);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("all-arg")))
+foo8 (int x)
+{
+  return (x + 8);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("all")))
+foo9 (int x)
+{
+  return (x + 9);
+}
+
+int main()
+{
+  result = foo1 (1);
+  result += foo2 (1);
+  result += foo3 (1);
+  result += foo4 (1);
+  result += foo5 (1);
+  result += foo6 (1);
+  result += foo7 (1);
+  result += foo8 (1);
+  result += foo9 (1);
+  assert (result == 54);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
new file mode 100644
index 0000000..7721e39
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=all" } */
+
+#include "zero-scratch-regs-10.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
new file mode 100644
index 0000000..25891ac
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */
+
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c
new file mode 100644
index 0000000..7c3d286
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */
+
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c
new file mode 100644
index 0000000..ba28c06
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */
+
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c
new file mode 100644
index 0000000..26679a4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=used" } */
+
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c
new file mode 100644
index 0000000..80f5bbb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */
+
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c
new file mode 100644
index 0000000..159f35c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */
+
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c
new file mode 100644
index 0000000..c1faaf0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */
+
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c
new file mode 100644
index 0000000..3f14bac
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c
@@ -0,0 +1,4 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=all" } */
+
+#include "zero-scratch-regs-1.c"
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
new file mode 100644
index 0000000..1e75795
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+int result __attribute__ ((zero_call_used_regs("all"))); /* { dg-error "attribute applies only to functions" } */
+int
+__attribute__ ((zero_call_used_regs("gpr-arg-all")))
+foo1 (int x) /* { dg-error "unrecognized 'zero_call_used_regs' attribute argument" } */
+{
+  return (x + 1);
+}
+int
+__attribute__ ((zero_call_used_regs(1)))
+foo2 (int x) /* { dg-error "argument not a string" } */
+{
+  return (x + 2);
+}
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c
new file mode 100644
index 0000000..9f61dc4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=used" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c
new file mode 100644
index 0000000..09048e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=skip" } */
+
+extern int foo (int) __attribute__ ((zero_call_used_regs("all-gpr")));
+
+int
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c
new file mode 100644
index 0000000..4862688
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c
@@ -0,0 +1,39 @@ 
+/* { dg-do run { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */
+
+struct S { int i; };
+__attribute__((const, noinline, noclone))
+struct S foo (int x)
+{
+  struct S s;
+  s.i = x;
+  return s;
+}
+
+int a[2048], b[2048], c[2048], d[2048];
+struct S e[2048];
+
+__attribute__((noinline, noclone)) void
+bar (void)
+{
+  int i;
+  for (i = 0; i < 1024; i++)
+    {
+      e[i] = foo (i);
+      a[i+2] = a[i] + a[i+1];
+      b[10] = b[10] + i;
+      c[i] = c[2047 - i];
+      d[i] = d[i + 1];
+    }
+}
+
+int
+main ()
+{
+  int i;
+  bar ();
+  for (i = 0; i < 1024; i++)
+    if (e[i].i != i)
+      __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c
new file mode 100644
index 0000000..500251b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c
@@ -0,0 +1,39 @@ 
+/* { dg-do run { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */
+
+struct S { int i; };
+__attribute__((const, noinline, noclone))
+struct S foo (int x)
+{
+  struct S s;
+  s.i = x;
+  return s;
+}
+
+int a[2048], b[2048], c[2048], d[2048];
+struct S e[2048];
+
+__attribute__((noinline, noclone)) void
+bar (void)
+{
+  int i;
+  for (i = 0; i < 1024; i++)
+    {
+      e[i] = foo (i);
+      a[i+2] = a[i] + a[i+1];
+      b[10] = b[10] + i;
+      c[i] = c[2047 - i];
+      d[i] = d[i + 1];
+    }
+}
+
+int
+main ()
+{
+  int i;
+  bar ();
+  for (i = 0; i < 1024; i++)
+    if (e[i].i != i)
+      __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c
new file mode 100644
index 0000000..8b058e3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */
+/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { target { ia32 } } } } */
+/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 15 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c
new file mode 100644
index 0000000..d4eaaf7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c
@@ -0,0 +1,19 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-times "vzeroall" 1 } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c
new file mode 100644
index 0000000..dd3bb90
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=skip" } */
+
+extern void foo (void) __attribute__ ((zero_call_used_regs("used")));
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c
new file mode 100644
index 0000000..e2274f6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all" } */
+
+extern void foo (void) __attribute__ ((zero_call_used_regs("skip")));
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c
new file mode 100644
index 0000000..7f5d153
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=used" } */
+
+int
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c
new file mode 100644
index 0000000..fe13d2b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=used -march=corei7" } */
+
+float
+foo (float z, float y, float x)
+{
+  return x + y;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c
new file mode 100644
index 0000000..205a532
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=used -march=corei7" } */
+
+float
+foo (float z, float y, float x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm2, %xmm2" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c
new file mode 100644
index 0000000..e046684
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c
@@ -0,0 +1,19 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c
new file mode 100644
index 0000000..4be8ff6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7" } */
+
+float
+foo (float z, float y, float x)
+{
+  return x + y;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" { target { ia32 } } } } */
+/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { target { ia32 } } } } */
+/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm1, %xmm\[0-9\]+" 14 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c
new file mode 100644
index 0000000..0eb34e0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=skip -march=corei7" } */
+
+__attribute__ ((zero_call_used_regs("used")))
+float
+foo (float z, float y, float x)
+{
+  return x + y;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c
new file mode 100644
index 0000000..0258c70
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "vzeroall" } } */
+/* { dg-final { scan-assembler-times "fldz" 8 } } */
+/* { dg-final { scan-assembler-times "fstp" 8 } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c
new file mode 100644
index 0000000..0625eb5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c
@@ -0,0 +1,29 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx512f" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "vzeroall" } } */
+/* { dg-final { scan-assembler-times "fldz" 8 } } */
+/* { dg-final { scan-assembler-times "fstp" 8 } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "kxorw\[ \t\]*%k0, %k0, %k0" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k1" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k2" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k3" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k4" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k5" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k6" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k7" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c
new file mode 100644
index 0000000..208633e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */
+
+int 
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c
new file mode 100644
index 0000000..21e82c6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */
+
+int 
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c
new file mode 100644
index 0000000..293d2fe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */
+
+int 
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" } } */
+/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm1" } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm2" } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm3" } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm4" } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm5" } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm6" } } */
+/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm7" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c
new file mode 100644
index 0000000..c34e6af
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */
+
+int 
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c
new file mode 100644
index 0000000..48b1f01
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mmmx -fzero-call-used-regs=all" } */
+/* { dg-require-effective-target ia32 } */
+
+__v2si ret_mmx (void)
+{
+  return (__v2si) { 123, 345 };
+}
+
+/* { dg-final { scan-assembler "pxor\[ \t\]*%mm1, %mm1" } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm2" } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm3" } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm4" } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm5" } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm6" } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm7" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c
new file mode 100644
index 0000000..8b5e1cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all" } */
+
+long double ret_x87 (void)
+{
+  return 1.1L;
+}
+
+/* { dg-final { scan-assembler-times "fldz" 7 } } */
+/* { dg-final { scan-assembler-times "fstp" 7 } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c
new file mode 100644
index 0000000..de71223
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=skip" } */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c
new file mode 100644
index 0000000..2a6f38f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2  -fzero-call-used-regs=all" } */
+
+_Complex long double ret_x87_cplx (void)
+{
+  return 1.1L + 1.2iL;
+}
+
+/* { dg-final { scan-assembler-times "fldz" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "fstp" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "fldz" 6 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "fstp" 6 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c
new file mode 100644
index 0000000..df71286
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mmmx -fzero-call-used-regs=all-arg" } */
+/* { dg-require-effective-target ia32 } */
+
+__v2si ret_mmx (void)
+{
+  return (__v2si) { 123, 345 };
+}
+
+/* { dg-final { scan-assembler "pxor\[ \t\]*%mm1, %mm1" } } */
+/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm2" } } */
+/* { dg-final { scan-assembler-not "movq\[ \t\]*%mm1, %mm\[34567\]" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c
new file mode 100644
index 0000000..ccfa441
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=skip" } */
+
+extern void foo (void) __attribute__ ((zero_call_used_regs("used-gpr")));
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c
new file mode 100644
index 0000000..6b46ca3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=skip" } */
+
+__attribute__ ((zero_call_used_regs("all-gpr")))
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c
new file mode 100644
index 0000000..0680f38
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */
+
+extern void foo (void) __attribute__ ((zero_call_used_regs("skip")));
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c
new file mode 100644
index 0000000..534defa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */
+
+int
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c
new file mode 100644
index 0000000..477bb19
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c
@@ -0,0 +1,19 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */
+
+int
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c
new file mode 100644
index 0000000..a305a60
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fzero-call-used-regs=skip" } */
+
+extern int foo (int) __attribute__ ((zero_call_used_regs("used-gpr")));
+
+int
+foo (int x)
+{
+  return x;
+}
+
+/* { dg-final { scan-assembler-not "vzeroall" } } */
+/* { dg-final { scan-assembler-not "%xmm" } } */
+/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 62e5b69..8afe8ee 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -592,6 +592,7 @@  extern rtl_opt_pass *make_pass_gcse2 (gcc::context *ctxt);
 extern rtl_opt_pass *make_pass_split_after_reload (gcc::context *ctxt);
 extern rtl_opt_pass *make_pass_thread_prologue_and_epilogue (gcc::context
 							     *ctxt);
+extern rtl_opt_pass *make_pass_zero_call_used_regs (gcc::context *ctxt);
 extern rtl_opt_pass *make_pass_stack_adjustments (gcc::context *ctxt);
 extern rtl_opt_pass *make_pass_sched_fusion (gcc::context *ctxt);
 extern rtl_opt_pass *make_pass_peephole2 (gcc::context *ctxt);