diff mbox series

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

Message ID BF1A66FD-19BF-4712-90AF-684E0E176A06@ORACLE.COM
State New
Headers show
Series [middle-end,i386,version,5] 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. 28, 2020, 11:54 p.m. UTC
Hi, 

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

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

1. Documentation change per Richard’s suggestion;
2. Use namespace for zero_regs_code;
3. Add more general testing cases per Richard’s suggestion;
4. I386 part, ST/MM register sets clearing per Uros’s suggestion. 
5. Add more i386 testing cases for ST/MM clearing per Uros’s suggestion.
6. Some minor style fixes. 

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

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

Thanks.

Qing

******The documentation (gcc.info):
'zero_call_used_regs ("CHOICE")'

     The 'zero_call_used_regs' attribute causes the compiler to zero a
     subset of all call-used registers at function return according to
     CHOICE.  This is used to increase the program security by either
     mitigating Return-Oriented Programming (ROP) or preventing
     information leak 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.

     The three basic values of CHOICE are:

        * 'skip' doesn't zero any call-used registers.

        * '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.

        * 'all' zeros all call-used registers.

     In addition to these three basic choices, it is possible to modify
     'used' or 'all' as follows:

        * Adding '-gpr' restricts the zeroing to general-purpose
          registers.

        * Adding '-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.

     The modifiers can be used individually or together.  If they are
     used together, they must appear in the order above.

     The full list of CHOICEs is therefore:

        * 'skip' doesn't zero any call-used register.

        * 'used' only zeros call-used registers that are used in the
          function.

        * 'all' zeros all call-used registers.

        * 'used-arg' only zeros used call-used registers that pass
          arguments.

        * 'used-gpr' only zeros used call-used general purpose
          registers.

        * 'used-gpr-arg' only zeros used call-used general purpose
          registers that pass arguments.

        * 'all-gpr-arg' zeros all call-used general purpose registers
          that pass arguments.

        * 'all-arg' zeros all call-used registers that pass arguments.

        * 'all-gpr' zeros all call-used general purpose registers.

     Among this list, 'used-gpr-arg', 'used-arg', 'all-gpr-arg', and
     'all-arg' are mainly used for ROP mitigation.

     The default for the attribute is controlled by
     '-fzero-call-used-regs’.

'-fzero-call-used-regs=CHOICE'
     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.

     The possible values of CHOICE are the same as for the
     'zero_call_used_regs' attribute (*note Function Attributes::).  The
     default is 'skip'.

     You can control this behavior for a specific function by using the
     function attribute 'zero_call_used_regs' (*note Function
     Attributes::).

******The changelog:

gcc/ChangeLog:

2020-10-28  Qing Zhao  <qing.zhao@oracle.com>
	    H.J.Lu  <hjl.tools@gmail.com>

	* common.opt: Add new option -fzero-call-used-regs
	* config/i386/i386.c (zero_call_used_regno_p): New function.
	(zero_call_used_regno_mode): Likewise.
	(zero_all_vector_registers): Likewise.
	(zero_all_st_registers): Likewise.
	(zero_all_mm_registers): Likewise.
	(ix86_zero_call_used_regs): Likewise.
	(TARGET_ZERO_CALL_USED_REGS): Define.
	* df-scan.c (df_epilogue_uses_p): New function.
	(df_get_exit_block_use_set): Replace EPILOGUE_USES with
	df_epilogue_uses_p.
	* df.h (df_epilogue_uses_p): Declare.
	* doc/extend.texi: Document the new zero_call_used_regs attribute.
	* doc/invoke.texi: Document the new -fzero-call-used-regs option.
	* doc/tm.texi: Regenerate.
	* doc/tm.texi.in (TARGET_ZERO_CALL_USED_REGS): New hook. 
	* emit-rtl.h (struct rtl_data): New fields zero_call_used_regs
	and must_be_zero_on_return.
	* flag-types.h (namespace zero_regs_code): New namespace.
	* function.c (gen_call_used_regs_seq): New function.
	(class pass_zero_call_used_regs): New class.
	(pass_zero_call_used_regs::execute): New function.
	(make_pass_zero_call_used_regs): New function.
	* optabs.c (expand_asm_reg_clobber_mem_blockage): New function.
	* optabs.h (expand_asm_reg_clobber_mem_blockage): Declare.
	* opts.c (zero_call_used_regs_opts): New structure array 
	initialization.  
	(parse_zero_call_used_regs_options): New function.
	(common_handle_option): Handle fzero-call-used-regs.
	* opts.h (zero_call_used_regs_opts): New structure array.
	* passes.def: Add new pass pass_zero_call_used_regs.
	* recog.c (valid_insn_p): New function.
	* recog.h (valid_insn_p): Declare.
	* resource.c (init_resource_info): Replace EPILOGUE_USES with
	df_epilogue_uses_p.
	* target.def (zero_call_used_regs): New hook.
	* targhooks.c (default_zero_call_used_regs): New function.
	* targhooks.h (default_zero_call_used_regs): Declare.
	* tree-pass.h (make_pass_zero_call_used_regs): Declare.

gcc/c-family/ChangeLog:

2020-10-28  Qing Zhao  <qing.zhao@oracle.com>
	    H.J.Lu  <hjl.tools@gmail.com>

	* c-attribs.c (c_common_attribute_table): Add new attribute
	zero_call_used_regs.
	(handle_zero_call_used_regs_attribute): New function.

gcc/testsuite/ChangeLog:

2020-10-28  Qing Zhao  <qing.zhao@oracle.com>
	    H.J.Lu  <hjl.tools@gmail.com>

	* c-c++-common/zero-scratch-regs-1.c: New test.
	* c-c++-common/zero-scratch-regs-10.c: New test.
	* c-c++-common/zero-scratch-regs-11.c: New test.
	* c-c++-common/zero-scratch-regs-2.c: New test.
	* c-c++-common/zero-scratch-regs-3.c: New test.
	* c-c++-common/zero-scratch-regs-4.c: New test.
	* c-c++-common/zero-scratch-regs-5.c: New test.
	* c-c++-common/zero-scratch-regs-6.c: New test.
	* c-c++-common/zero-scratch-regs-7.c: New test.
	* c-c++-common/zero-scratch-regs-8.c: New test.
	* c-c++-common/zero-scratch-regs-9.c: New test.
	* c-c++-common/zero-scratch-regs-attr-usages.c: New test.
	* gcc.target/i386/zero-scratch-regs-1.c: New test.
	* gcc.target/i386/zero-scratch-regs-10.c: New test.
	* gcc.target/i386/zero-scratch-regs-11.c: New test.
	* gcc.target/i386/zero-scratch-regs-12.c: New test.
	* gcc.target/i386/zero-scratch-regs-13.c: New test.
	* gcc.target/i386/zero-scratch-regs-14.c: New test.
	* gcc.target/i386/zero-scratch-regs-15.c: New test.
	* gcc.target/i386/zero-scratch-regs-16.c: New test.
	* gcc.target/i386/zero-scratch-regs-17.c: New test.
	* gcc.target/i386/zero-scratch-regs-18.c: New test.
	* gcc.target/i386/zero-scratch-regs-19.c: New test.
	* gcc.target/i386/zero-scratch-regs-2.c: New test.
	* gcc.target/i386/zero-scratch-regs-20.c: New test.
	* gcc.target/i386/zero-scratch-regs-21.c: New test.
	* gcc.target/i386/zero-scratch-regs-22.c: New test.
	* gcc.target/i386/zero-scratch-regs-23.c: New test.
	* gcc.target/i386/zero-scratch-regs-24.c: New test.
	* gcc.target/i386/zero-scratch-regs-25.c: New test.
	* gcc.target/i386/zero-scratch-regs-26.c: New test.
	* gcc.target/i386/zero-scratch-regs-27.c: New test.
	* gcc.target/i386/zero-scratch-regs-28.c: New test.
	* gcc.target/i386/zero-scratch-regs-29.c: New test.
	* gcc.target/i386/zero-scratch-regs-30.c: New test.
	* gcc.target/i386/zero-scratch-regs-31.c: New test.
	* gcc.target/i386/zero-scratch-regs-3.c: New test.
	* gcc.target/i386/zero-scratch-regs-4.c: New test.
	* gcc.target/i386/zero-scratch-regs-5.c: New test.
	* gcc.target/i386/zero-scratch-regs-6.c: New test.
	* gcc.target/i386/zero-scratch-regs-7.c: New test.
	* gcc.target/i386/zero-scratch-regs-8.c: New test.
	* gcc.target/i386/zero-scratch-regs-9.c: New test.
*******The patch:

From 257dd69611a08eff9f3ee129c305236fedc5c4f9 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 5th 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                           |  49 ++++
 gcc/common.opt                                     |   8 +
 gcc/config/i386/i386.c                             | 294 +++++++++++++++++++++
 gcc/df-scan.c                                      |  12 +-
 gcc/df.h                                           |   1 +
 gcc/doc/extend.texi                                |  90 +++++++
 gcc/doc/invoke.texi                                |  15 +-
 gcc/doc/tm.texi                                    |  12 +
 gcc/doc/tm.texi.in                                 |   2 +
 gcc/emit-rtl.h                                     |   6 +
 gcc/flag-types.h                                   |  22 ++
 gcc/function.c                                     | 192 +++++++++++++-
 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  |  92 +++++++
 gcc/testsuite/c-c++-common/zero-scratch-regs-2.c   |  15 ++
 gcc/testsuite/c-c++-common/zero-scratch-regs-3.c   |  15 ++
 gcc/testsuite/c-c++-common/zero-scratch-regs-4.c   |  15 ++
 gcc/testsuite/c-c++-common/zero-scratch-regs-5.c   |  15 ++
 gcc/testsuite/c-c++-common/zero-scratch-regs-6.c   |  15 ++
 gcc/testsuite/c-c++-common/zero-scratch-regs-7.c   |  15 ++
 gcc/testsuite/c-c++-common/zero-scratch-regs-8.c   |  15 ++
 gcc/testsuite/c-c++-common/zero-scratch-regs-9.c   |  15 ++
 .../c-c++-common/zero-scratch-regs-attr-usages.c   |  10 +
 .../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, 1729 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

Comments

Uros Bizjak Oct. 29, 2020, 7:48 a.m. UTC | #1
On Thu, Oct 29, 2020 at 12:55 AM Qing Zhao <QING.ZHAO@oracle.com> wrote:
>
> Hi,
>
> This is the 5th version of the implementation of patch -fzero-call-used-regs.
>
> The major change compared to the previous version (4th version) are:
>
> 1. Documentation change per Richard’s suggestion;
> 2. Use namespace for zero_regs_code;
> 3. Add more general testing cases per Richard’s suggestion;
> 4. I386 part, ST/MM register sets clearing per Uros’s suggestion.
> 5. Add more i386 testing cases for ST/MM clearing per Uros’s suggestion.
> 6. Some minor style fixes.
>
> I have tested this new GCC on both x86 and arm64, no regression.
>
> Please let me know whether it’s ready for stage 1 gcc11?
>
> Thanks.
>
> Qing
>
> ******The documentation (gcc.info):
> 'zero_call_used_regs ("CHOICE")'
>
>      The 'zero_call_used_regs' attribute causes the compiler to zero a
>      subset of all call-used registers at function return according to
>      CHOICE.  This is used to increase the program security by either
>      mitigating Return-Oriented Programming (ROP) or preventing
>      information leak 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.
>
>      The three basic values of CHOICE are:
>
>         * 'skip' doesn't zero any call-used registers.
>
>         * '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.
>
>         * 'all' zeros all call-used registers.
>
>      In addition to these three basic choices, it is possible to modify
>      'used' or 'all' as follows:
>
>         * Adding '-gpr' restricts the zeroing to general-purpose
>           registers.
>
>         * Adding '-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.
>
>      The modifiers can be used individually or together.  If they are
>      used together, they must appear in the order above.
>
>      The full list of CHOICEs is therefore:
>
>         * 'skip' doesn't zero any call-used register.
>
>         * 'used' only zeros call-used registers that are used in the
>           function.
>
>         * 'all' zeros all call-used registers.
>
>         * 'used-arg' only zeros used call-used registers that pass
>           arguments.
>
>         * 'used-gpr' only zeros used call-used general purpose
>           registers.
>
>         * 'used-gpr-arg' only zeros used call-used general purpose
>           registers that pass arguments.
>
>         * 'all-gpr-arg' zeros all call-used general purpose registers
>           that pass arguments.
>
>         * 'all-arg' zeros all call-used registers that pass arguments.
>
>         * 'all-gpr' zeros all call-used general purpose registers.
>
>      Among this list, 'used-gpr-arg', 'used-arg', 'all-gpr-arg', and
>      'all-arg' are mainly used for ROP mitigation.
>
>      The default for the attribute is controlled by
>      '-fzero-call-used-regs’.
>
> '-fzero-call-used-regs=CHOICE'
>      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.
>
>      The possible values of CHOICE are the same as for the
>      'zero_call_used_regs' attribute (*note Function Attributes::).  The
>      default is 'skip'.
>
>      You can control this behavior for a specific function by using the
>      function attribute 'zero_call_used_regs' (*note Function
>      Attributes::).
>
> ******The changelog:
>
> gcc/ChangeLog:
>
> 2020-10-28  Qing Zhao  <qing.zhao@oracle.com>
>             H.J.Lu  <hjl.tools@gmail.com>
>
>         * common.opt: Add new option -fzero-call-used-regs
>         * config/i386/i386.c (zero_call_used_regno_p): New function.
>         (zero_call_used_regno_mode): Likewise.
>         (zero_all_vector_registers): Likewise.
>         (zero_all_st_registers): Likewise.
>         (zero_all_mm_registers): Likewise.
>         (ix86_zero_call_used_regs): Likewise.
>         (TARGET_ZERO_CALL_USED_REGS): Define.
>         * df-scan.c (df_epilogue_uses_p): New function.
>         (df_get_exit_block_use_set): Replace EPILOGUE_USES with
>         df_epilogue_uses_p.
>         * df.h (df_epilogue_uses_p): Declare.
>         * doc/extend.texi: Document the new zero_call_used_regs attribute.
>         * doc/invoke.texi: Document the new -fzero-call-used-regs option.
>         * doc/tm.texi: Regenerate.
>         * doc/tm.texi.in (TARGET_ZERO_CALL_USED_REGS): New hook.
>         * emit-rtl.h (struct rtl_data): New fields zero_call_used_regs
>         and must_be_zero_on_return.
>         * flag-types.h (namespace zero_regs_code): New namespace.
>         * function.c (gen_call_used_regs_seq): New function.
>         (class pass_zero_call_used_regs): New class.
>         (pass_zero_call_used_regs::execute): New function.
>         (make_pass_zero_call_used_regs): New function.
>         * optabs.c (expand_asm_reg_clobber_mem_blockage): New function.
>         * optabs.h (expand_asm_reg_clobber_mem_blockage): Declare.
>         * opts.c (zero_call_used_regs_opts): New structure array
>         initialization.
>         (parse_zero_call_used_regs_options): New function.
>         (common_handle_option): Handle fzero-call-used-regs.
>         * opts.h (zero_call_used_regs_opts): New structure array.
>         * passes.def: Add new pass pass_zero_call_used_regs.
>         * recog.c (valid_insn_p): New function.
>         * recog.h (valid_insn_p): Declare.
>         * resource.c (init_resource_info): Replace EPILOGUE_USES with
>         df_epilogue_uses_p.
>         * target.def (zero_call_used_regs): New hook.
>         * targhooks.c (default_zero_call_used_regs): New function.
>         * targhooks.h (default_zero_call_used_regs): Declare.
>         * tree-pass.h (make_pass_zero_call_used_regs): Declare.
>
> gcc/c-family/ChangeLog:
>
> 2020-10-28  Qing Zhao  <qing.zhao@oracle.com>
>             H.J.Lu  <hjl.tools@gmail.com>
>
>         * c-attribs.c (c_common_attribute_table): Add new attribute
>         zero_call_used_regs.
>         (handle_zero_call_used_regs_attribute): New function.
>
> gcc/testsuite/ChangeLog:
>
> 2020-10-28  Qing Zhao  <qing.zhao@oracle.com>
>             H.J.Lu  <hjl.tools@gmail.com>
>
>         * c-c++-common/zero-scratch-regs-1.c: New test.
>         * c-c++-common/zero-scratch-regs-10.c: New test.
>         * c-c++-common/zero-scratch-regs-11.c: New test.
>         * c-c++-common/zero-scratch-regs-2.c: New test.
>         * c-c++-common/zero-scratch-regs-3.c: New test.
>         * c-c++-common/zero-scratch-regs-4.c: New test.
>         * c-c++-common/zero-scratch-regs-5.c: New test.
>         * c-c++-common/zero-scratch-regs-6.c: New test.
>         * c-c++-common/zero-scratch-regs-7.c: New test.
>         * c-c++-common/zero-scratch-regs-8.c: New test.
>         * c-c++-common/zero-scratch-regs-9.c: New test.
>         * c-c++-common/zero-scratch-regs-attr-usages.c: New test.
>         * gcc.target/i386/zero-scratch-regs-1.c: New test.
>         * gcc.target/i386/zero-scratch-regs-10.c: New test.
>         * gcc.target/i386/zero-scratch-regs-11.c: New test.
>         * gcc.target/i386/zero-scratch-regs-12.c: New test.
>         * gcc.target/i386/zero-scratch-regs-13.c: New test.
>         * gcc.target/i386/zero-scratch-regs-14.c: New test.
>         * gcc.target/i386/zero-scratch-regs-15.c: New test.
>         * gcc.target/i386/zero-scratch-regs-16.c: New test.
>         * gcc.target/i386/zero-scratch-regs-17.c: New test.
>         * gcc.target/i386/zero-scratch-regs-18.c: New test.
>         * gcc.target/i386/zero-scratch-regs-19.c: New test.
>         * gcc.target/i386/zero-scratch-regs-2.c: New test.
>         * gcc.target/i386/zero-scratch-regs-20.c: New test.
>         * gcc.target/i386/zero-scratch-regs-21.c: New test.
>         * gcc.target/i386/zero-scratch-regs-22.c: New test.
>         * gcc.target/i386/zero-scratch-regs-23.c: New test.
>         * gcc.target/i386/zero-scratch-regs-24.c: New test.
>         * gcc.target/i386/zero-scratch-regs-25.c: New test.
>         * gcc.target/i386/zero-scratch-regs-26.c: New test.
>         * gcc.target/i386/zero-scratch-regs-27.c: New test.
>         * gcc.target/i386/zero-scratch-regs-28.c: New test.
>         * gcc.target/i386/zero-scratch-regs-29.c: New test.
>         * gcc.target/i386/zero-scratch-regs-30.c: New test.
>         * gcc.target/i386/zero-scratch-regs-31.c: New test.
>         * gcc.target/i386/zero-scratch-regs-3.c: New test.
>         * gcc.target/i386/zero-scratch-regs-4.c: New test.
>         * gcc.target/i386/zero-scratch-regs-5.c: New test.
>         * gcc.target/i386/zero-scratch-regs-6.c: New test.
>         * gcc.target/i386/zero-scratch-regs-7.c: New test.
>         * gcc.target/i386/zero-scratch-regs-8.c: New test.
>         * gcc.target/i386/zero-scratch-regs-9.c: New test.

OK for x86 part.

Thanks,
Uros.
Richard Sandiford Oct. 29, 2020, 11:09 a.m. UTC | #2
Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
> +/* 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;
> +    }
> +
> +  if (TREE_CODE (id) != STRING_CST)
> +    {
> +      error_at (DECL_SOURCE_LOCATION (decl),
> +		"attribute %qE arguments not a string", name);

The existing message for this seems to be:

  "%qE argument not a string"

(which seems a bit terse, but hey)

> +      *no_add_attrs = true;
> +    }
> +
> +  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 zero_call_used_regs attribute: %qs",
> +		TREE_STRING_POINTER (id));

The attribute name needs to be quoted, and it would be good if it
wasn't hard-coded into the string:

      error_at (DECL_SOURCE_LOCATION (decl),
		"unrecognized %qE attribute argument %qs", name,
		TREE_STRING_POINTER (id));

> @@ -228,6 +228,10 @@ unsigned int flag_sanitize_coverage
>  Variable
>  bool dump_base_name_prefixed = false
>  
> +; What subset of registers should be zeroed

Think it would be useful to add “ on function return.”.

> +Variable
> +unsigned int flag_zero_call_used_regs
> +
>  ###
>  Driver
>  
> 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..b011c17 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -3992,6 +3992,96 @@ 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 at function return according to
> +@var{choice}.

Suggest dropping “according to @var{choice}” here, since it's now
disconnected with the part that talks about what @var{choice} is.

> +This is used to increase the program security by either mitigating

s/the program security/program security/

> +Return-Oriented Programming (ROP) or preventing information leak

leakage

(FWIW, I'm not sure “mitigating ROP” is really correct usage, but I don't
have any better suggestions.)

> +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''.

Reading it back, perhaps it would be better to put this whole paragraph
in a footnote immediately after the first use of “call-used registers”,
i.e.

…call-used registers@footnote{A ``call-used'' register…}…

It obviously breaks the flow when reading the raw .texi, but I think it
reads better in the final version.

> +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.

Maybe s/GCC/the @var{choice} parameter/.

> +
> +The three basic values of @var{choice} are:

After which, I think this should be part of the previous paragraph.

> +@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:
> +
> +@itemize @bullet
> +@item
> +@samp{skip} doesn't zero any call-used register.
> +
> +@item
> +@samp{used} only zeros call-used registers that are used in the function.
> +
> +@item
> +@samp{all} zeros all call-used registers.
> +
> +@item
> +@samp{used-arg} only zeros used call-used registers that pass arguments.
> +
> +@item
> +@samp{used-gpr} only zeros used call-used general purpose registers.
> +
> +@item
> +@samp{used-gpr-arg} only zeros used call-used general purpose registers that
> +pass arguments.
> +
> +@item
> +@samp{all-gpr-arg} zeros all call-used general purpose registers that pass
> +arguments.
> +
> +@item
> +@samp{all-arg} zeros all call-used registers that pass arguments.
> +
> +@item
> +@samp{all-gpr} zeros all call-used general purpose registers.
> +@end itemize

By using a table, I meant:

@table @samp
@item skip
…

@item used
…
etc.
@end @table

Did you try that and think the output looked odd?

I think the order should be more consistent.  E.g. “all” should be listed
with the other “all” options, and the ordering of foos for “used-foo”
and “all-foo” should be the same.  So maybe:

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

> +Among this list, @samp{used-gpr-arg}, @samp{used-arg}, @samp{all-gpr-arg},

IMO s/Among/Of/ reads slightly better.

> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index c049932..c9e3128 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 the program

s/the program/program/

> +security by either mitigating Return-Oriented Programming (ROP) or
> +preventing information leak through registers.

s/leak/leakage/

> @@ -173,6 +173,9 @@ 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;
> +

Typo: regsiters.  But I don't think we need to add this to crtl.
It's just data that's passed between pass_zero_call_used_regs::execute
and gen_call_used_regs_seq.

>    /* How many NOP insns to place at each function entry by default.  */
>    unsigned short patch_area_size;
>  
> @@ -285,6 +287,24 @@ enum sanitize_code {
>  				  | SANITIZE_BOUNDS_STRICT
>  };
>  
> +/* Different settings for zeroing subset of registers.  */
> +namespace  zero_regs_code {

Should only be one space after “namespace”.  Having “code” in the name
surprised me, think “flags” would be better.

> @@ -5815,6 +5817,101 @@ make_prologue_seq (void)
>    return seq;
>  }
>  
> +/* Emit a sequence of insns to zero the call-used registers before RET.  */
> +using namespace zero_regs_code;

Making the “using” file-wide is too much, since the file has a lot of code
unrelated to this feature.  It should go in the function body instead.

> +
> +static void
> +gen_call_used_regs_seq (rtx_insn *ret)
> +{
> +  bool gpr_only = true;
> +  bool used_only = true;
> +  bool arg_only = 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 gpr_only is true, only zero call-used registers that are
> +     general-purpose registers; if used_only 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
> +     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;

Guess it would be nice to be consistent about which side the “only”
goes on.  FWIW, I don't mind which way: GPR_ONLY etc. would be
OK with me if you prefer that.

> +
> +  /* 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.  */
> +
> +  /* 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 (gpr_only
> +	  && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
> +	continue;
> +      if (used_only && !df_regs_ever_live_p (regno))
> +	continue;
> +      if (arg_only && !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 +6583,100 @@ 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)
> +{
> +  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));

The “= …” line should be indented by only two extra spaces.  Although
in this case it fits on two lines anyway:

  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.  */

“from the function attribute”  Might be worth adding “We have already
filtered out invalid attribute values.”, to explain why there's (rightly)
no failure path.

> +  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)
> +	  {
> +	    attr_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;

Having two variables seems unnecessarily complicated.  I think the
attribute code should assign directly to “zero_regs_type”.

> +
> +  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;
> +
> +  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);

As noted above, we could just pass “zero_regs_type” here rather than
store it in crtl.

> @@ -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");

"-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..6756f57
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
> @@ -0,0 +1,92 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fzero-call-used-regs=all" } */
> +
> +#include <assert.h>
> +int result = 0;
> …

I think this should just #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..73c3794
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
> @@ -0,0 +1,15 @@
> +/* { 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;
> +}

Similarly these can just #include "zero-scratch-regs-1.c".  This makes
it easier to update the tests in a consistent way in future (if necessary).

> 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..c60e946
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
> @@ -0,0 +1,10 @@
> +/* { 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" } */
> +{
> +  return (x + 1);
> +}

Could you also add a test for the string constant check?  E.g. with
__attribute__ ((zero_call_used_regs(1))).

Thanks,
Richard
Qing Zhao Oct. 29, 2020, 2:46 p.m. UTC | #3
> On Oct 29, 2020, at 6:09 AM, Richard Sandiford <richard.sandiford@arm.com> wrote:
> 
> Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
>> +/* 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;
>> +    }
>> +
>> +  if (TREE_CODE (id) != STRING_CST)
>> +    {
>> +      error_at (DECL_SOURCE_LOCATION (decl),
>> +		"attribute %qE arguments not a string", name);
> 
> The existing message for this seems to be:
> 
>  "%qE argument not a string"
> 
> (which seems a bit terse, but hey)

Okay.
> 
>> +      *no_add_attrs = true;
>> +    }
>> +
>> +  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 zero_call_used_regs attribute: %qs",
>> +		TREE_STRING_POINTER (id));
> 
> The attribute name needs to be quoted, and it would be good if it
> wasn't hard-coded into the string:
> 
>      error_at (DECL_SOURCE_LOCATION (decl),
> 		"unrecognized %qE attribute argument %qs", name,
> 		TREE_STRING_POINTER (id));
Okay.
> 
>> @@ -228,6 +228,10 @@ unsigned int flag_sanitize_coverage
>> Variable
>> bool dump_base_name_prefixed = false
>> 
>> +; What subset of registers should be zeroed
> 
> Think it would be useful to add “ on function return.”.
Okay.
> 
>> +Variable
>> +unsigned int flag_zero_call_used_regs
>> +
>> ###
>> Driver
>> 
>> 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..b011c17 100644
>> --- a/gcc/doc/extend.texi
>> +++ b/gcc/doc/extend.texi
>> @@ -3992,6 +3992,96 @@ 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 at function return according to
>> +@var{choice}.
> 
> Suggest dropping “according to @var{choice}” here, since it's now
> disconnected with the part that talks about what @var{choice} is.
Okay
> 
>> +This is used to increase the program security by either mitigating
> 
> s/the program security/program security/
Okay
> 
>> +Return-Oriented Programming (ROP) or preventing information leak
> 
> leakage
> 
> (FWIW, I'm not sure “mitigating ROP” is really correct usage, but I don't
> have any better suggestions.)

Do you mean whether “mitigating ROP’ is one of the major purpose of this new feature?

The initial main motivation of the new feature is for mitigating ROP. And the reason for only zeroing
argument subset of the register is also for mitigating ROP.

> 
>> +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''.
> 
> Reading it back, perhaps it would be better to put this whole paragraph
> in a footnote immediately after the first use of “call-used registers”,
> i.e.
> 
> …call-used registers@footnote{A ``call-used'' register…}…
> 
> It obviously breaks the flow when reading the raw .texi, but I think it
> reads better in the final version.

Okay.
> 
>> +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.
> 
> Maybe s/GCC/the @var{choice} parameter/.
Okay.
> 
>> +
>> +The three basic values of @var{choice} are:
> 
> After which, I think this should be part of the previous paragraph.

Don’t understand here, could you explain a little bit more?

> 
>> +@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:
>> +
>> +@itemize @bullet
>> +@item
>> +@samp{skip} doesn't zero any call-used register.
>> +
>> +@item
>> +@samp{used} only zeros call-used registers that are used in the function.
>> +
>> +@item
>> +@samp{all} zeros all call-used registers.
>> +
>> +@item
>> +@samp{used-arg} only zeros used call-used registers that pass arguments.
>> +
>> +@item
>> +@samp{used-gpr} only zeros used call-used general purpose registers.
>> +
>> +@item
>> +@samp{used-gpr-arg} only zeros used call-used general purpose registers that
>> +pass arguments.
>> +
>> +@item
>> +@samp{all-gpr-arg} zeros all call-used general purpose registers that pass
>> +arguments.
>> +
>> +@item
>> +@samp{all-arg} zeros all call-used registers that pass arguments.
>> +
>> +@item
>> +@samp{all-gpr} zeros all call-used general purpose registers.
>> +@end itemize
> 
> By using a table, I meant:
> 
> @table @samp
> @item skip
> …
> 
> @item used
> …
> etc.
> @end @table
> 
> Did you try that and think the output looked odd?

I will try to see what’s the output.
> 
> I think the order should be more consistent.  E.g. “all” should be listed
> with the other “all” options, and the ordering of foos for “used-foo”
> and “all-foo” should be the same.  So maybe:
> 
> - skip
> - used
> - used-arg
> - used-gpr
> - used-gpr-arg
> - all
> - all-arg
> - all-gpr
> - all-gpr-arg

Okay
> 
>> +Among this list, @samp{used-gpr-arg}, @samp{used-arg}, @samp{all-gpr-arg},
> 
> IMO s/Among/Of/ reads slightly better.
Okay.
> 
>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>> index c049932..c9e3128 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 the program
> 
> s/the program/program/
okay

> 
>> +security by either mitigating Return-Oriented Programming (ROP) or
>> +preventing information leak through registers.
> 
> s/leak/leakage/
Okay

> 
>> @@ -173,6 +173,9 @@ 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;
>> +
> 
> Typo: regsiters.  But I don't think we need to add this to crtl.
> It's just data that's passed between pass_zero_call_used_regs::execute
> and gen_call_used_regs_seq.
You are right.  
Previously, this part of code was put into “gate()”, so adding this to crtl was needed at that time.
But now it is not needed anymore.

> 
>>   /* How many NOP insns to place at each function entry by default.  */
>>   unsigned short patch_area_size;
>> 
>> @@ -285,6 +287,24 @@ enum sanitize_code {
>> 				  | SANITIZE_BOUNDS_STRICT
>> };
>> 
>> +/* Different settings for zeroing subset of registers.  */
>> +namespace  zero_regs_code {
> 
> Should only be one space after “namespace”.  Having “code” in the name
> surprised me, think “flags” would be better.
Okay.
> 
>> @@ -5815,6 +5817,101 @@ make_prologue_seq (void)
>>   return seq;
>> }
>> 
>> +/* Emit a sequence of insns to zero the call-used registers before RET.  */
>> +using namespace zero_regs_code;
> 
> Making the “using” file-wide is too much, since the file has a lot of code
> unrelated to this feature.  It should go in the function body instead.
Okay
> 
>> +
>> +static void
>> +gen_call_used_regs_seq (rtx_insn *ret)
>> +{
>> +  bool gpr_only = true;
>> +  bool used_only = true;
>> +  bool arg_only = 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 gpr_only is true, only zero call-used registers that are
>> +     general-purpose registers; if used_only 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
>> +     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;
> 
> Guess it would be nice to be consistent about which side the “only”
> goes on.  FWIW, I don't mind which way: GPR_ONLY etc. would be
> OK with me if you prefer that.
The current names are okay for me.
> 
>> +
>> +  /* 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.  */
>> +
>> +  /* 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 (gpr_only
>> +	  && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
>> +	continue;
>> +      if (used_only && !df_regs_ever_live_p (regno))
>> +	continue;
>> +      if (arg_only && !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 +6583,100 @@ 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)
>> +{
>> +  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));
> 
> The “= …” line should be indented by only two extra spaces.  Although
> in this case it fits on two lines anyway:
> 
>  tree attr_zero_regs = lookup_attribute ("zero_call_used_regs",
> 					  DECL_ATTRIBUTES (fun->decl));
Okay
> 
>> +  /* Get the type of zero_call_used_regs from function attribute.  */
> 
> “from the function attribute”  Might be worth adding “We have already
> filtered out invalid attribute values.”, to explain why there's (rightly)
> no failure path.
Okay
> 
>> +  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)
>> +	  {
>> +	    attr_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;
> 
> Having two variables seems unnecessarily complicated.  I think the
> attribute code should assign directly to “zero_regs_type”.
You are right.
> 
>> +
>> +  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;
>> +
>> +  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);
> 
> As noted above, we could just pass “zero_regs_type” here rather than
> store it in crtl.

Okay.
> 
>> @@ -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");
> 
> "-fzero-call-used_regs”
Okay
> 
>> +	      }
>> +	    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..6756f57
>> --- /dev/null
>> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
>> @@ -0,0 +1,92 @@
>> +/* { dg-do run } */
>> +/* { dg-options "-O2 -fzero-call-used-regs=all" } */
>> +
>> +#include <assert.h>
>> +int result = 0;
>> …
> 
> I think this should just #include "zero-scratch-regs-10.c”.

Okay
> 
>> 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..73c3794
>> --- /dev/null
>> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
>> @@ -0,0 +1,15 @@
>> +/* { 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;
>> +}
> 
> Similarly these can just #include "zero-scratch-regs-1.c".  This makes
> it easier to update the tests in a consistent way in future (if necessary).

Okay, thanks for the suggestion. 
> 
>> 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..c60e946
>> --- /dev/null
>> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
>> @@ -0,0 +1,10 @@
>> +/* { 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" } */
>> +{
>> +  return (x + 1);
>> +}
> 
> Could you also add a test for the string constant check?  E.g. with
> __attribute__ ((zero_call_used_regs(1))).
Okay.

Thanks.

Qing
> 
> Thanks,
> Richard
Qing Zhao Oct. 29, 2020, 4:24 p.m. UTC | #4
Hi, Richard,


Now, the documentation (gcc.info) is like following, let me know any issue there:

thanks.

Qing
==============

'zero_call_used_regs ("CHOICE")'

     The 'zero_call_used_regs' attribute causes the compiler to zero a
     subset of all call-used registers(1) at function return.  This is
     used to increase program security by either mitigating
     Return-Oriented Programming (ROP) 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, CHOICE parameter provides a
     flexible way to choose the subset of the call-used registers to be
     zeroed.

     The three basic values of CHOICE are:

        * 'skip' doesn't zero any call-used registers.

        * '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.

        * 'all' zeros all call-used registers.

     In addition to these three basic choices, it is possible to modify
     'used' or 'all' as follows:

        * Adding '-gpr' restricts the zeroing to general-purpose
          registers.

        * Adding '-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.

     The modifiers can be used individually or together.  If they are
     used together, they must appear in the order above.

     The full list of CHOICEs is therefore:

     'skip'
          doesn't zero any call-used register.

     'used'
          only zeros call-used registers that are used in the function.

     'used-gpr'
          only zeros call-used general purpose registers that are used
          in the function.

     'used-arg'
          only zeros call-used registers that are used in the function
          and pass arguments.

     'used-gpr-arg'
          only zeros call-used general purpose registers that are used
          in the function and pass arguments.

     'all'
          zeros all call-used registers.

     'all-gpr'
          zeros all call-used general purpose registers.

     'all-arg'
          zeros all call-used registers that pass arguments.

     'all-gpr-arg'
          zeros all call-used general purpose registers that pass
          arguments.

     Of this list, 'used-arg', 'used-gpr-arg', 'all-arg', and
     'all-gpr-arg' are mainly used for ROP mitigation.

     The default for the attribute is controlled by
     '-fzero-call-used-regs'.

   ---------- Footnotes ----------

   (1) 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”.


'-fzero-call-used-regs=CHOICE'
     Zero call-used registers at function return to increase program
     security by either mitigating Return-Oriented Programming (ROP) or
     preventing information leakage through registers.

     The possible values of CHOICE are the same as for the
     'zero_call_used_regs' attribute (*note Function Attributes::).  The
     default is 'skip'.

     You can control this behavior for a specific function by using the
     function attribute 'zero_call_used_regs' (*note Function
     Attributes::).
Richard Sandiford Oct. 29, 2020, 6:02 p.m. UTC | #5
Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
>>> +Return-Oriented Programming (ROP) or preventing information leak
>> 
>> leakage
>> 
>> (FWIW, I'm not sure “mitigating ROP” is really correct usage, but I don't
>> have any better suggestions.)
>
> Do you mean whether “mitigating ROP’ is one of the major purpose of this new feature?

No, I meant just the English usage.  E.g., I think you mitigate the
damage caused by earthquakes rather than mitigate earthquakes themselves.
But I could be wrong.  It's not a word I use very often ;-)

>>> +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.
>> 
>> Maybe s/GCC/the @var{choice} parameter/.
> Okay.
>> 
>>> +
>>> +The three basic values of @var{choice} are:
>> 
>> After which, I think this should be part of the previous paragraph.
>
> Don’t understand here, could you explain a little bit more?

I meant:

In order to satisfy users with different security needs and control the
run-time overhead at the same time, @var{choice} provides a flexible way
to choose the subset of the call-used registers to be zeroed.  The three
basic values of @var{choice} are:

>>> +  /* If gpr_only is true, only zero call-used registers that are
>>> +     general-purpose registers; if used_only 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
>>> +     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;
>> 
>> Guess it would be nice to be consistent about which side the “only”
>> goes on.  FWIW, I don't mind which way: GPR_ONLY etc. would be
>> OK with me if you prefer that.
> The current names are okay for me.

OK.  But I think one of them should change to match the other.
E.g. either the local variable should be “only_gpr” or the
flag should be “GPR_ONLY”.

Thanks,
Richard
Richard Sandiford Oct. 29, 2020, 6:06 p.m. UTC | #6
Qing Zhao <QING.ZHAO@ORACLE.COM> writes:
> Now, the documentation (gcc.info) is like following, let me know any issue there:

Yeah, looks good apart from merging

>      In order to satisfy users with different security needs and control
>      the run-time overhead at the same time, CHOICE parameter provides a
>      flexible way to choose the subset of the call-used registers to be
>      zeroed.
>
>      The three basic values of CHOICE are:

this into a single paragraph.

Thanks,
Richard
Segher Boessenkool Oct. 29, 2020, 7:31 p.m. UTC | #7
On Thu, Oct 29, 2020 at 06:02:58PM +0000, Richard Sandiford wrote:
> Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
> >>> +Return-Oriented Programming (ROP) or preventing information leak
> >> 
> >> leakage
> >> 
> >> (FWIW, I'm not sure “mitigating ROP” is really correct usage, but I don't
> >> have any better suggestions.)
> >
> > Do you mean whether “mitigating ROP’ is one of the major purpose of this new feature?
> 
> No, I meant just the English usage.  E.g., I think you mitigate the
> damage caused by earthquakes rather than mitigate earthquakes themselves.
> But I could be wrong.  It's not a word I use very often ;-)

"Mitigating ROP attacks" is a phrase often used in the literature, sadly
(what is really meant is not "mitigating the effects of ROP attacks",
but simply "making ROP attacks less likely to succeed" -- it is almost
always a binary thing, either it succeeds or it doesn't).


Segher
Qing Zhao Oct. 29, 2020, 8:10 p.m. UTC | #8
> On Oct 29, 2020, at 1:02 PM, Richard Sandiford <richard.sandiford@arm.com> wrote:
> 
> Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
>>>> +Return-Oriented Programming (ROP) or preventing information leak
>>> 
>>> leakage
>>> 
>>> (FWIW, I'm not sure “mitigating ROP” is really correct usage, but I don't
>>> have any better suggestions.)
>> 
>> Do you mean whether “mitigating ROP’ is one of the major purpose of this new feature?
> 
> No, I meant just the English usage.  E.g., I think you mitigate the
> damage caused by earthquakes rather than mitigate earthquakes themselves.
> But I could be wrong.  It's not a word I use very often ;-)

Okay.
I see. 
> 
>>>> +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.
>>> 
>>> Maybe s/GCC/the @var{choice} parameter/.
>> Okay.
>>> 
>>>> +
>>>> +The three basic values of @var{choice} are:
>>> 
>>> After which, I think this should be part of the previous paragraph.
>> 
>> Don’t understand here, could you explain a little bit more?
> 
> I meant:
> 
> In order to satisfy users with different security needs and control the
> run-time overhead at the same time, @var{choice} provides a flexible way
> to choose the subset of the call-used registers to be zeroed.  The three
> basic values of @var{choice} are:
> 

Oh. :-)

>>>> +  /* If gpr_only is true, only zero call-used registers that are
>>>> +     general-purpose registers; if used_only 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
>>>> +     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;
>>> 
>>> Guess it would be nice to be consistent about which side the “only”
>>> goes on.  FWIW, I don't mind which way: GPR_ONLY etc. would be
>>> OK with me if you prefer that.
>> The current names are okay for me.
> 
> OK.  But I think one of them should change to match the other.
> E.g. either the local variable should be “only_gpr” or the
> flag should be “GPR_ONLY”.
Okay, I see what you mean, will make them consistent.

Qing
> 
> Thanks,
> Richard
Qing Zhao Oct. 29, 2020, 8:13 p.m. UTC | #9
> On Oct 29, 2020, at 1:06 PM, Richard Sandiford <richard.sandiford@arm.com> wrote:
> 
> Qing Zhao <QING.ZHAO@ORACLE.COM> writes:
>> Now, the documentation (gcc.info) is like following, let me know any issue there:
> 
> Yeah, looks good apart from merging
> 
>>     In order to satisfy users with different security needs and control
>>     the run-time overhead at the same time, CHOICE parameter provides a
>>     flexible way to choose the subset of the call-used registers to be
>>     zeroed.
>> 
>>     The three basic values of CHOICE are:
> 
> this into a single paragraph.

Okay, will do.

Qing
> 
> Thanks,
> Richard
Qing Zhao Oct. 29, 2020, 8:20 p.m. UTC | #10
Hi, Segher,

> On Oct 29, 2020, at 2:31 PM, Segher Boessenkool <segher@kernel.crashing.org> wrote:
> 
> On Thu, Oct 29, 2020 at 06:02:58PM +0000, Richard Sandiford wrote:
>> Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
>>>>> +Return-Oriented Programming (ROP) or preventing information leak
>>>> 
>>>> leakage
>>>> 
>>>> (FWIW, I'm not sure “mitigating ROP” is really correct usage, but I don't
>>>> have any better suggestions.)
>>> 
>>> Do you mean whether “mitigating ROP’ is one of the major purpose of this new feature?
>> 
>> No, I meant just the English usage.  E.g., I think you mitigate the
>> damage caused by earthquakes rather than mitigate earthquakes themselves.
>> But I could be wrong.  It's not a word I use very often ;-)
> 
> "Mitigating ROP attacks" is a phrase often used in the literature, sadly
> (what is really meant is not "mitigating the effects of ROP attacks",
> but simply "making ROP attacks less likely to succeed" -- it is almost
> always a binary thing, either it succeeds or it doesn't).

Thanks.

I will change “Mitigating ROP” to “Mitigating ROP attacks” in the documentation. 

Qing
> 
> 
> Segher
diff mbox series

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index c779d13..1b05e8c 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,51 @@  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;
+    }
+
+  if (TREE_CODE (id) != STRING_CST)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"attribute %qE arguments not a string", name);
+      *no_add_attrs = true;
+    }
+
+  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 zero_call_used_regs attribute: %qs",
+		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..4a13f32 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
+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..b011c17 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3992,6 +3992,96 @@  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 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
+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.
+
+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:
+
+@itemize @bullet
+@item
+@samp{skip} doesn't zero any call-used register.
+
+@item
+@samp{used} only zeros call-used registers that are used in the function.
+
+@item
+@samp{all} zeros all call-used registers.
+
+@item
+@samp{used-arg} only zeros used call-used registers that pass arguments.
+
+@item
+@samp{used-gpr} only zeros used call-used general purpose registers.
+
+@item
+@samp{used-gpr-arg} only zeros used call-used general purpose registers that
+pass arguments.
+
+@item
+@samp{all-gpr-arg} zeros all call-used general purpose registers that pass
+arguments.
+
+@item
+@samp{all-arg} zeros all call-used registers that pass arguments.
+
+@item
+@samp{all-gpr} zeros all call-used general purpose registers.
+@end itemize
+
+Among this list, @samp{used-gpr-arg}, @samp{used-arg}, @samp{all-gpr-arg},
+and @samp{all-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..c9e3128 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 the program
+security by either mitigating Return-Oriented Programming (ROP) or
+preventing information leak 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..d7bdb66 100644
--- a/gcc/emit-rtl.h
+++ b/gcc/emit-rtl.h
@@ -173,6 +173,9 @@  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;
 
@@ -310,6 +313,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..8807635 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_code {
+  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..77f0b5d 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,101 @@  make_prologue_seq (void)
   return seq;
 }
 
+/* Emit a sequence of insns to zero the call-used registers before RET.  */
+using namespace zero_regs_code;
+
+static void
+gen_call_used_regs_seq (rtx_insn *ret)
+{
+  bool gpr_only = true;
+  bool used_only = true;
+  bool arg_only = 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 gpr_only is true, only zero call-used registers that are
+     general-purpose registers; if used_only 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
+     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;
+
+  /* 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.  */
+
+  /* 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 (gpr_only
+	  && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
+	continue;
+      if (used_only && !df_regs_ever_live_p (regno))
+	continue;
+      if (arg_only && !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 +6583,100 @@  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)
+{
+  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));
+
+  /* Get the type of zero_call_used_regs from function attribute.  */
+  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)
+	  {
+	    attr_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;
+
+  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);
+    }
+
+  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..7cd097a 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_code;
+/* -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..88eef00 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..6756f57
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c
@@ -0,0 +1,92 @@ 
+/* { 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;
+}
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..73c3794
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
@@ -0,0 +1,15 @@ 
+/* { 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;
+}
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..d3db757
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c
@@ -0,0 +1,15 @@ 
+/* { 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;
+}
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..029aa79
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c
@@ -0,0 +1,15 @@ 
+/* { 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;
+}
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..509ce20
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c
@@ -0,0 +1,15 @@ 
+/* { 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;
+}
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..56d8063
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c
@@ -0,0 +1,15 @@ 
+/* { 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;
+}
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..40378ed
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c
@@ -0,0 +1,15 @@ 
+/* { 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;
+}
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..7bdef95
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c
@@ -0,0 +1,15 @@ 
+/* { 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;
+}
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..032da1a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c
@@ -0,0 +1,15 @@ 
+/* { 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;
+}
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..c60e946
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c
@@ -0,0 +1,10 @@ 
+/* { 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" } */
+{
+  return (x + 1);
+}
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);