PING [Patch][Middle-end]Add -fzero-call-used-regs=[skip|used-gpr|all-gpr|used|all]

Richard Biener rguenther@suse.de
Tue Aug 4 07:35:00 GMT 2020


On Mon, 3 Aug 2020, Qing Zhao wrote:

> Hi, Uros,
> 
> Thanks a lot for your review on X86 parts.
> 
> Hi, Richard,
> 
> Could you please take a look at the middle-end part to see whether the 
> rewritten addressed your previous concern?

I have a few comments below - I'm not sure I'm qualified to fully
review the rest though.

> Thanks a lot.
> 
> Qing
> 
> 
> > On Jul 31, 2020, at 12:57 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> > 
> > 
> > 22:05, tor., 28. jul. 2020 je oseba Qing Zhao <QING.ZHAO@oracle.com <mailto:QING.ZHAO@oracle.com>> napisala:
> > >
> > >
> > > Richard and Uros,
> > >
> > > Could you please review the change that H.J and I rewrote based on your comments in the previous round of discussion?
> > >
> > > This patch is a nice security enhancement for GCC that has been requested by security people for quite some time.  
> > >
> > > Thanks a lot for your time.
> > 
> > I'll be away from the keyboard for the next week, but the patch needs a middle end approval first.
> > 
> > That said, x86 parts looks OK.
> > 
> > 
> 
> > Uros.
> > > Qing
> > >
> > > > On Jul 14, 2020, at 9:45 AM, Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org <mailto:gcc-patches@gcc.gnu.org>> wrote:
> > > > 
> > > > Hi, Gcc team,
> > > > 
> > > > This patch is a follow-up on the previous patch and corresponding discussion:
> > > > https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545101.html <https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545101.html> <https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545101.html <https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545101.html>>
> > > > 
> > > > From the previous round of discussion, the major issues raised were:
> > > > 
> > > > A. should be rewritten by using regsets infrastructure.  
> > > > B. Put the patch into middle-end instead of x86 backend. 
> > > > 
> > > > This new patch is rewritten based on the above 2 comments.  The major changes compared to the previous patch are:
> > > > 
> > > > 1. Change the names of the option and attribute from 
> > > > -mzero-caller-saved-regs=[skip|used-gpr|all-gpr|used|all]  and zero_caller_saved_regs("skip|used-gpr|all-gpr||used|all”)
> > > > to:
> > > > -fzero-call-used-regs=[skip|used-gpr|all-gpr|used|all]   and  zero_call_used_regs("skip|used-gpr|all-gpr||used|all”) 
> > > > Add the new option and  new attribute in general. 
> > > > 2. The main code generation part is moved from i386 backend to middle-end;
> > > > 3. Add 4 target-hooks;
> > > > 4. Implement these 4 target-hooks on i386 backend. 
> > > > 5. On a target that does not implement the target hook, issue error for the new option, issue warning for the new attribute.
> > > > 
> > > > The patch is as following:
> > > > 
> > > > [PATCH] Add -fzero-call-used-regs=[skip|used-gpr|all-gpr|used|all]
> > > > command-line option and
> > > > zero_call_used_regs("skip|used-gpr|all-gpr||used|all") function attribue:
> > > > 
> > > >  1. -fzero-call-used-regs=skip and zero_call_used_regs("skip")
> > > > 
> > > >  Don't zero call-used registers upon function return.

Does a return via EH unwinding also constitute a function return?  I
think you may want to have a finally handler or support in the unwinder
for this?  Then there's abnormal return via longjmp & friends, I guess
there's nothing that can be done there besides patching glibc?

In general I am missing reasoning as why to use -fzero-call-used-regs=
in the documentation, that is, what is the thread model and what are
the guarantees?  Is there any point zeroing registers when spill slots
are left populated with stale register contents?  How do I (and why
would I want to?) ensure that there's no information leak from the
implementation of 'foo' to their callers?  Do I need to compile all
of 'foo' and functions called from 'foo' with -fzero-call-used-regs=
or is it enough to annotate API boundaries I want to proptect with
zero_call_used_regs("...")?

Again - what's the intended use (and how does it fulful anything useful
for that case)?

> > > >  2. -fzero-call-used-regs=used-gpr and zero_call_used_regs("used-gpr")
> > > > 
> > > >  Zero used call-used general purpose registers upon function return.
> > > > 
> > > >  3. -fzero-call-used-regs=all-gpr and zero_call_used_regs("all-gpr")
> > > > 
> > > >  Zero all call-used general purpose registers upon function return.
> > > > 
> > > >  4. -fzero-call-used-regs=used and zero_call_used_regs("used")
> > > > 
> > > >  Zero used call-used registers upon function return.
> > > > 
> > > >  5. -fzero-call-used-regs=all and zero_call_used_regs("all")
> > > > 
> > > >  Zero all call-used registers upon function return.
> > > > 
> > > > The feature is implemented in middle-end. But currently is only valid on X86.
> > > > 
> > > > Tested on x86-64 and aarch64 with bootstrapping GCC trunk, making
> > > > -fzero-call-used-regs=used-gpr, -fzero-call-used-regs=all-gpr
> > > > -fzero-call-used-regs=used, and -fzero-call-used-regs=all enabled
> > > > by default on x86-64.
> > > > 
> > > > Please take a look and let me know any more comment?
> > > > 
> > > > thanks.
> > > > 
> > > > Qing
> > > > 
> > > > 
> > > > ====================================
> > > > 
> > > > gcc/ChangeLog:
> > > > 
> > > > 2020-07-13  qing zhao  <qing.zhao@oracle.com <mailto:qing.zhao@oracle.com> <mailto:qing.zhao@oracle.com <mailto:qing.zhao@oracle.com>>>
> > > > 2020-07-13  H.J. Lu <hjl.tools@gmail.com <mailto:hjl.tools@gmail.com> <mailto:hjl.tools@gmail.com <mailto:hjl.tools@gmail.com>>>
> > > > 
> > > >       * common.opt: Add new option -fzero-call-used-regs.
> > > >       * config/i386/i386.c (ix86_zero_call_used_regno_p): New function.
> > > >       (ix86_zero_call_used_regno_mode): Likewise.
> > > >       (ix86_zero_all_vector_registers): Likewise.
> > > >       (ix86_expand_prologue): Replace gen_prologue_use with
> > > >       gen_pro_epilogue_use.
> > > >       (TARGET_ZERO_CALL_USED_REGNO_P): Define.
> > > >       (TARGET_ZERO_CALL_USED_REGNO_MODE): Define.
> > > >       (TARGET_PRO_EPILOGUE_USE): Define.
> > > >       (TARGET_ZERO_ALL_VECTOR_REGISTERS): Define.
> > > >       * config/i386/i386.md: Replace UNSPECV_PROLOGUE_USE
> > > >       with UNSPECV_PRO_EPILOGUE_USE.
> > > >       * coretypes.h (enum zero_call_used_regs): New type.
> > > >       * 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 <http://tm.texi.in/> (TARGET_ZERO_CALL_USED_REGNO_P): New hook.
> > > >       (TARGET_ZERO_CALL_USED_REGNO_MODE): Likewise.
> > > >       (TARGET_PRO_EPILOGUE_USE): Likewise.
> > > >       (TARGET_ZERO_ALL_VECTOR_REGISTERS): Likewise.
> > > >       * function.c (is_live_reg_at_exit): New function.
> > > >       (gen_call_used_regs_seq): Likewise.
> > > >       (make_epilogue_seq): Call gen_call_used_regs_seq.
> > > >       * function.h (is_live_reg_at_exit): Declare.
> > > >       * target.def (zero_call_used_regno_p): New hook.
> > > >       (zero_call_used_regno_mode): Likewise.
> > > >       (pro_epilogue_use): Likewise.
> > > >       (zero_all_vector_registers): Likewise.
> > > >       * targhooks.c (default_zero_call_used_regno_p): New function.
> > > >       (default_zero_call_used_regno_mode): Likewise.
> > > >       * targhooks.h (default_zero_call_used_regno_p): Declare.
> > > >       (default_zero_call_used_regno_mode): Declare.
> > > >       * toplev.c (process_options): Issue errors when -fzero-call-used-regs
> > > >       is used on targets that do not support it.
> > > >       * tree-core.h (struct tree_decl_with_vis): New field 
> > > >       zero_call_used_regs_type.
> > > >       * tree.h (DECL_ZERO_CALL_USED_REGS): New macro.
> > > > 
> > > > gcc/c-family/ChangeLog:
> > > > 
> > > > 2020-07-13  qing zhao  <qing.zhao@oracle.com <mailto:qing.zhao@oracle.com> <mailto:qing.zhao@oracle.com <mailto:qing.zhao@oracle.com>>>
> > > > 2020-07-13  H.J. Lu <hjl.tools@gmail.com <mailto:hjl.tools@gmail.com> <mailto:hjl.tools@gmail.com <mailto: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/c/ChangeLog:
> > > > 
> > > > 2020-07-13  qing zhao  <qing.zhao@oracle.com <mailto:qing.zhao@oracle.com> <mailto:qing.zhao@oracle.com <mailto:qing.zhao@oracle.com>>>
> > > > 2020-07-13  H.J. Lu <hjl.tools@gmail.com <mailto:hjl.tools@gmail.com> <mailto:hjl.tools@gmail.com <mailto:hjl.tools@gmail.com>>>
> > > > 
> > > >       * c-decl.c (merge_decls): Merge zero_call_used_regs_type.
> > > > 
> > > > gcc/testsuite/ChangeLog:
> > > > 
> > > > 2020-07-13  qing zhao  <qing.zhao@oracle.com <mailto:qing.zhao@oracle.com> <mailto:qing.zhao@oracle.com <mailto:qing.zhao@oracle.com>>>
> > > > 2020-07-13  H.J. Lu <hjl.tools@gmail.com <mailto:hjl.tools@gmail.com> <mailto:hjl.tools@gmail.com <mailto:hjl.tools@gmail.com>>>
> > > > 
> > > >       * c-c++-common/zero-scratch-regs-1.c: New test.
> > > >       * c-c++-common/zero-scratch-regs-2.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-1.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-10.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-11.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-12.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-13.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-14.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-15.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-16.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-17.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-18.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-19.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-2.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-20.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-21.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-22.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-23.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-3.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-4.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-5.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-6.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-7.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-8.c: Likewise.
> > > >       * gcc.target/i386/zero-scratch-regs-9.c: Likewise.
> > > > 
> > > > ---
> > > > gcc/c-family/c-attribs.c                           |  68 ++++++++++
> > > > gcc/c/c-decl.c                                     |   4 +
> > > > gcc/common.opt                                     |  23 ++++
> > > > gcc/config/i386/i386.c                             |  58 ++++++++-
> > > > gcc/config/i386/i386.md                            |   6 +-
> > > > gcc/coretypes.h                                    |  10 ++
> > > > gcc/doc/extend.texi                                |  11 ++
> > > > gcc/doc/invoke.texi                                |  13 +-
> > > > gcc/doc/tm.texi                                    |  27 ++++
> > > > gcc/doc/tm.texi.in <http://tm.texi.in/>                                 |   8 ++
> > > > gcc/function.c                                     | 145 +++++++++++++++++++++
> > > > gcc/function.h                                     |   2 +
> > > > gcc/target.def                                     |  33 +++++
> > > > gcc/targhooks.c                                    |  17 +++
> > > > gcc/targhooks.h                                    |   3 +
> > > > gcc/testsuite/c-c++-common/zero-scratch-regs-1.c   |   3 +
> > > > gcc/testsuite/c-c++-common/zero-scratch-regs-2.c   |   4 +
> > > > .../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         |  19 +++
> > > > .../gcc.target/i386/zero-scratch-regs-23.c         |  19 +++
> > > > .../gcc.target/i386/zero-scratch-regs-3.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/toplev.c                                       |   9 ++
> > > > gcc/tree-core.h                                    |   6 +-
> > > > gcc/tree.h                                         |   5 +
> > > > 43 files changed, 866 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-2.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-3.c
> > > > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c
> > > > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c
> > > > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c
> > > > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c
> > > > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c
> > > > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c
> > > > 
> > > > diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
> > > > index 3721483..cc93d6f 100644
> > > > --- a/gcc/c-family/c-attribs.c
> > > > +++ b/gcc/c-family/c-attribs.c
> > > > @@ -136,6 +136,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_fnspec_attribute (tree *, tree, tree, int, bool *);
> > > > static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *);
> > > > static tree handle_returns_nonnull_attribute (tree *, tree, tree, int, bool *);
> > > > @@ -434,6 +436,9 @@ 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 (marking of builtins and runtime functions) only.
> > > >     The name contains space to prevent its usage in source code.  */
> > > >  { "fn spec",               1, 1, false, true, true, false,
> > > > @@ -4506,6 +4511,69 @@ 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_attris)
> > > > +{
> > > > +  tree decl = *node;
> > > > +  tree id = TREE_VALUE (args);
> > > > +  enum zero_call_used_regs zero_call_used_regs_type = zero_call_used_regs_unset;
> > > > +
> > > > +  if (TREE_CODE (decl) != FUNCTION_DECL)
> > > > +    {
> > > > +      error_at (DECL_SOURCE_LOCATION (decl),
> > > > +             "%qE attribute applies only to functions", name);
> > > > +      *no_add_attris = true;
> > > > +      return NULL_TREE;
> > > > +    }
> > > > +  else if (DECL_INITIAL (decl))
> > > > +    {
> > > > +      error_at (DECL_SOURCE_LOCATION (decl),
> > > > +             "cannot set %qE attribute after definition", name);

Why's that?

> > > > +      *no_add_attris = true;
> > > > +      return NULL_TREE;
> > > > +    }
> > > > +
> > > > +  if (TREE_CODE (id) != STRING_CST)
> > > > +    {
> > > > +      error ("attribute %qE arguments not a string", name);
> > > > +      *no_add_attris = true;
> > > > +      return NULL_TREE;
> > > > +    }
> > > > +
> > > > +  if (!targetm.calls.pro_epilogue_use)
> > > > +    {
> > > > +      warning (OPT_Wattributes, "%qE attribute directive ignored", name);
> > > > +      return NULL_TREE;
> > > > +    }
> > > > +
> > > > +  if (strcmp (TREE_STRING_POINTER (id), "skip") == 0)
> > > > +    zero_call_used_regs_type = zero_call_used_regs_skip;
> > > > +  else if (strcmp (TREE_STRING_POINTER (id), "used-gpr") == 0)
> > > > +    zero_call_used_regs_type = zero_call_used_regs_used_gpr;
> > > > +  else if (strcmp (TREE_STRING_POINTER (id), "all-gpr") == 0)
> > > > +    zero_call_used_regs_type = zero_call_used_regs_all_gpr;
> > > > +  else if (strcmp (TREE_STRING_POINTER (id), "used") == 0)
> > > > +    zero_call_used_regs_type = zero_call_used_regs_used;
> > > > +  else if (strcmp (TREE_STRING_POINTER (id), "all") == 0)
> > > > +    zero_call_used_regs_type = zero_call_used_regs_all;
> > > > +  else
> > > > +    {
> > > > +      error ("attribute %qE argument must be one of %qs, %qs, %qs, %qs, or %qs",
> > > > +          name, "skip", "used-gpr", "all-gpr", "used", "all");
> > > > +      *no_add_attris = true;
> > > > +      return NULL_TREE;
> > > > +    }
> > > > +
> > > > +  DECL_ZERO_CALL_USED_REGS (decl) = zero_call_used_regs_type;
> > > > +
> > > > +  return NULL_TREE;
> > > > +}
> > > > +
> > > > /* Handle a "returns_nonnull" attribute; arguments as in
> > > >   struct attribute_spec.handler.  */
> > > > 
> > > > diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> > > > index 81bd2ee..ded1880 100644
> > > > --- a/gcc/c/c-decl.c
> > > > +++ b/gcc/c/c-decl.c
> > > > @@ -2681,6 +2681,10 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
> > > >         DECL_IS_NOVOPS (newdecl) |= DECL_IS_NOVOPS (olddecl);
> > > >       }
> > > > 
> > > > +      /* Merge the zero_call_used_regs_type information.  */
> > > > +      if (TREE_CODE (newdecl) == FUNCTION_DECL)
> > > > +     DECL_ZERO_CALL_USED_REGS (newdecl) = DECL_ZERO_CALL_USED_REGS (olddecl);
> > > > +

If you need this (see below) then likely cp/* needs similar adjustment
so do other places in the middle-end (function cloning, etc)

> > > >      /* Merge the storage class information.  */
> > > >      merge_weak (newdecl, olddecl);
> > > > 
> > > > diff --git a/gcc/common.opt b/gcc/common.opt
> > > > index df8af36..19900f9 100644
> > > > --- a/gcc/common.opt
> > > > +++ b/gcc/common.opt
> > > > @@ -3083,6 +3083,29 @@ 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 Enum(zero_call_used_regs) Var(flag_zero_call_used_regs) Init(zero_call_used_regs_skip)
> > > > +Clear call-used registers upon function return.
> > > > +
> > > > +Enum
> > > > +Name(zero_call_used_regs) Type(enum zero_call_used_regs)
> > > > +Known choices of clearing call-used registers upon function return (for use with the -fzero-call-used-regs= option):
> > > > +
> > > > +EnumValue
> > > > +Enum(zero_call_used_regs) String(skip) Value(zero_call_used_regs_skip)
> > > > +
> > > > +EnumValue
> > > > +Enum(zero_call_used_regs) String(used-gpr) Value(zero_call_used_regs_used_gpr)
> > > > +
> > > > +EnumValue
> > > > +Enum(zero_call_used_regs) String(all-gpr) Value(zero_call_used_regs_all_gpr)
> > > > +
> > > > +EnumValue
> > > > +Enum(zero_call_used_regs) String(used) Value(zero_call_used_regs_used)
> > > > +
> > > > +EnumValue
> > > > +Enum(zero_call_used_regs) String(all) Value(zero_call_used_regs_all)
> > > > +
> > > > 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 5c373c0..fd1aa9c 100644
> > > > --- a/gcc/config/i386/i386.c
> > > > +++ b/gcc/config/i386/i386.c
> > > > @@ -3551,6 +3551,48 @@ ix86_function_value_regno_p (const unsigned int regno)
> > > >  return false;
> > > > }
> > > > 
> > > > +/* TARGET_ZERO_CALL_USED_REGNO_P.  */
> > > > +
> > > > +static bool
> > > > +ix86_zero_call_used_regno_p (const unsigned int regno,
> > > > +                          bool gpr_only)
> > > > +{
> > > > +  return GENERAL_REGNO_P (regno) || (!gpr_only && SSE_REGNO_P (regno));
> > > > +}
> > > > +
> > > > +/* TARGET_ZERO_CALL_USED_REGNO_MODE.  */
> > > > +
> > > > +static machine_mode
> > > > +ix86_zero_call_used_regno_mode (const unsigned int regno, machine_mode)
> > > > +{
> > > > +  /* 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.  */
> > > > +  return GENERAL_REGNO_P (regno) ? SImode : V4SFmode;
> > > > +}
> > > > +
> > > > +/* TARGET_ZERO_ALL_VECTOR_REGISTERS.  */
> > > > +
> > > > +static rtx
> > > > +ix86_zero_all_vector_registers (bool used_only)
> > > > +{
> > > > +  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)))))
> > > > +     && (!this_target_hard_regs->x_call_used_regs[regno]
> > > > +         || fixed_regs[regno]
> > > > +         || is_live_reg_at_exit (regno)
> > > > +         || (used_only && !df_regs_ever_live_p (regno))))
> > > > +      return NULL;
> > > > +
> > > > +  return gen_avx_vzeroall ();
> > > > +}
> > > > +
> > > > /* 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;
> > > > @@ -8513,7 +8555,7 @@ ix86_expand_prologue (void)
> > > >      insn = emit_insn (gen_set_got (pic));
> > > >      RTX_FRAME_RELATED_P (insn) = 1;
> > > >      add_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL_RTX);
> > > > -      emit_insn (gen_prologue_use (pic));
> > > > +      emit_insn (gen_pro_epilogue_use (pic));
> > > >      /* Deleting already emmitted SET_GOT if exist and allocated to
> > > >        REAL_PIC_OFFSET_TABLE_REGNUM.  */
> > > >      ix86_elim_entry_set_got (pic);
> > > > @@ -8542,7 +8584,7 @@ ix86_expand_prologue (void)
> > > >     Further, prevent alloca modifications to the stack pointer from being
> > > >     combined with prologue modifications.  */
> > > >  if (TARGET_SEH)
> > > > -    emit_insn (gen_prologue_use (stack_pointer_rtx));
> > > > +    emit_insn (gen_pro_epilogue_use (stack_pointer_rtx));
> > > > }
> > > > 
> > > > /* Emit code to restore REG using a POP insn.  */
> > > > @@ -23319,6 +23361,18 @@ 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_REGNO_P
> > > > +#define TARGET_ZERO_CALL_USED_REGNO_P ix86_zero_call_used_regno_p
> > > > +
> > > > +#undef TARGET_ZERO_CALL_USED_REGNO_MODE
> > > > +#define TARGET_ZERO_CALL_USED_REGNO_MODE ix86_zero_call_used_regno_mode
> > > > +
> > > > +#undef TARGET_PRO_EPILOGUE_USE
> > > > +#define TARGET_PRO_EPILOGUE_USE gen_pro_epilogue_use
> > > > +
> > > > +#undef TARGET_ZERO_ALL_VECTOR_REGISTERS
> > > > +#define TARGET_ZERO_ALL_VECTOR_REGISTERS ix86_zero_all_vector_registers
> > > > +
> > > > #undef TARGET_PROMOTE_FUNCTION_MODE
> > > > #define TARGET_PROMOTE_FUNCTION_MODE ix86_promote_function_mode
> > > > 
> > > > diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
> > > > index d0ecd9e..e7df59f 100644
> > > > --- a/gcc/config/i386/i386.md
> > > > +++ b/gcc/config/i386/i386.md
> > > > @@ -194,7 +194,7 @@
> > > >  UNSPECV_STACK_PROBE
> > > >  UNSPECV_PROBE_STACK_RANGE
> > > >  UNSPECV_ALIGN
> > > > -  UNSPECV_PROLOGUE_USE
> > > > +  UNSPECV_PRO_EPILOGUE_USE
> > > >  UNSPECV_SPLIT_STACK_RETURN
> > > >  UNSPECV_CLD
> > > >  UNSPECV_NOPS
> > > > @@ -13525,8 +13525,8 @@
> > > > 
> > > > ;; As USE insns aren't meaningful after reload, this is used instead
> > > > ;; to prevent deleting instructions setting registers for PIC code
> > > > -(define_insn "prologue_use"
> > > > -  [(unspec_volatile [(match_operand 0)] UNSPECV_PROLOGUE_USE)]
> > > > +(define_insn "pro_epilogue_use"
> > > > +  [(unspec_volatile [(match_operand 0)] UNSPECV_PRO_EPILOGUE_USE)]
> > > >  ""
> > > >  ""
> > > >  [(set_attr "length" "0")])
> > > > diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> > > > index 6b6cfcd..e56d6ec 100644
> > > > --- a/gcc/coretypes.h
> > > > +++ b/gcc/coretypes.h
> > > > @@ -418,6 +418,16 @@ enum symbol_visibility
> > > >  VISIBILITY_INTERNAL
> > > > };
> > > > 
> > > > +/* Zero call-used registers type.  */
> > > > +enum zero_call_used_regs {
> > > > +  zero_call_used_regs_unset = 0,
> > > > +  zero_call_used_regs_skip,
> > > > +  zero_call_used_regs_used_gpr,
> > > > +  zero_call_used_regs_all_gpr,
> > > > +  zero_call_used_regs_used,
> > > > +  zero_call_used_regs_all
> > > > +};
> > > > +
> > > > /* enums used by the targetm.excess_precision hook.  */
> > > > 
> > > > enum flt_eval_method
> > > > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > > > index c800b74..b32c55f 100644
> > > > --- a/gcc/doc/extend.texi
> > > > +++ b/gcc/doc/extend.texi
> > > > @@ -3984,6 +3984,17 @@ 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
> > > > +call-used registers at function return according to @var{choice}.
> > > > +@samp{skip} doesn't zero call-used registers. @samp{used-gpr} zeros
> > > > +call-used general purpose registers which are used in funciton.
> > > > +@samp{all-gpr} zeros all call-used general purpose registers.
> > > > +@samp{used} zeros call-used registers which are used in function.
> > > > +@samp{all} zeros all call-used registers.  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 09bcc5b..da02686 100644
> > > > --- a/gcc/doc/invoke.texi
> > > > +++ b/gcc/doc/invoke.texi
> > > > @@ -542,7 +542,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}
> > > > 
> > > > @@ -12273,6 +12273,17 @@ 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 according to
> > > > +@var{choice}.  @samp{skip}, which is the default, doesn't zero
> > > > +call-used registers.  @samp{used-gpr} zeros call-used general purpose
> > > > +registers which are used in function.  @samp{all-gpr} zeros all
> > > > +call-used registers.  @samp{used} zeros call-used registers which
> > > > +are used in function.  @samp{all} zeros all call-used registers.  You
> > > > +can control this behavior for a specific function by using the function
> > > > +attribute @code{zero_call_used_regs}.  @xref{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 6e7d9dc..43dddd3 100644
> > > > --- a/gcc/doc/tm.texi
> > > > +++ b/gcc/doc/tm.texi
> > > > @@ -4571,6 +4571,22 @@ should recognize only the caller's register numbers.
> > > > If this hook is not defined, then FUNCTION_VALUE_REGNO_P will be used.
> > > > @end deftypefn
> > > > 
> > > > +@deftypefn {Target Hook} bool TARGET_ZERO_CALL_USED_REGNO_P (const unsigned int @var{regno}, bool @var{general_reg_only_p})
> > > > +A target hook that returns @code{true} if @var{regno} is the number of a
> > > > +call used register.  If @var{general_reg_only_p} is @code{true},
> > > > +@var{regno} must be the number of a hard general register.
> > > > +
> > > > +If this hook is not defined, then default_zero_call_used_regno_p will be used.
> > > > +@end deftypefn
> > > > +
> > > > +@deftypefn {Target Hook} machine_mode TARGET_ZERO_CALL_USED_REGNO_MODE (const unsigned int @var{regno}, machine_mode @var{mode})
> > > > +A target hook that returns a mode of suitable to zero the register for the
> > > > +call used register @var{regno} in @var{mode}.
> > > > +
> > > > +If this hook is not defined, then default_zero_call_used_regno_mode will be
> > > > +used.
> > > > +@end deftypefn
> > > > +
> > > > @defmac APPLY_RESULT_SIZE
> > > > Define this macro if @samp{untyped_call} and @samp{untyped_return}
> > > > need more space than is implied by @code{FUNCTION_VALUE_REGNO_P} for
> > > > @@ -12043,6 +12059,17 @@ argument list due to stack realignment.  Return @code{NULL} if no DRAP
> > > > is needed.
> > > > @end deftypefn
> > > > 
> > > > +@deftypefn {Target Hook} rtx TARGET_PRO_EPILOGUE_USE (rtx @var{reg})
> > > > +This hook should return a UNSPEC_VOLATILE rtx to mark a register in use to
> > > > +prevent deleting register setting instructions in proprologue and epilogue.
> > > > +@end deftypefn
> > > > +
> > > > +@deftypefn {Target Hook} rtx TARGET_ZERO_ALL_VECTOR_REGISTERS (bool @var{used_only})
> > > > +This hook should return an rtx to zero all vector registers at function
> > > > +exit.  If @var{used_only} is @code{true}, only used vector registers should
> > > > +be zeroed.  Return @code{NULL} if possible
> > > > +@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 <http://tm.texi.in/> b/gcc/doc/tm.texi.in <http://tm.texi.in/>
> > > > index 3be984b..bee917a 100644
> > > > --- a/gcc/doc/tm.texi.in <http://tm.texi.in/>
> > > > +++ b/gcc/doc/tm.texi.in <http://tm.texi.in/>
> > > > @@ -3430,6 +3430,10 @@ for a new target instead.
> > > > 
> > > > @hook TARGET_FUNCTION_VALUE_REGNO_P
> > > > 
> > > > +@hook TARGET_ZERO_CALL_USED_REGNO_P
> > > > +
> > > > +@hook TARGET_ZERO_CALL_USED_REGNO_MODE
> > > > +
> > > > @defmac APPLY_RESULT_SIZE
> > > > Define this macro if @samp{untyped_call} and @samp{untyped_return}
> > > > need more space than is implied by @code{FUNCTION_VALUE_REGNO_P} for
> > > > @@ -8109,6 +8113,10 @@ and the associated definitions of those functions.
> > > > 
> > > > @hook TARGET_GET_DRAP_RTX
> > > > 
> > > > +@hook TARGET_PRO_EPILOGUE_USE
> > > > +
> > > > +@hook TARGET_ZERO_ALL_VECTOR_REGISTERS
> > > > +
> > > > @hook TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
> > > > 
> > > > @hook TARGET_CONST_ANCHOR
> > > > diff --git a/gcc/function.c b/gcc/function.c
> > > > index 9eee9b5..9908530 100644
> > > > --- a/gcc/function.c
> > > > +++ b/gcc/function.c
> > > > @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
> > > > #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"
> > > > @@ -5808,6 +5809,147 @@ make_prologue_seq (void)
> > > >  return seq;
> > > > }
> > > > 
> > > > +/* Check whether the hard register REGNO is live at the exit block
> > > > + * of the current routine.  */
> > > > +bool
> > > > +is_live_reg_at_exit (unsigned int regno)
> > > > +{
> > > > +  edge e;
> > > > +  edge_iterator ei;
> > > > +
> > > > +  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
> > > > +    {
> > > > +      bitmap live_out = df_get_live_out (e->src);
> > > > +      if (REGNO_REG_SET_P (live_out, regno))
> > > > +     return true;
> > > > +    }
> > > > +
> > > > +  return false;
> > > > +}
> > > > +
> > > > +/* Emit a sequence of insns to zero the call-used-registers for the current
> > > > + * function.  */

No '*' on the continuation line

> > > > +
> > > > +static void
> > > > +gen_call_used_regs_seq (void)
> > > > +{
> > > > +  if (!targetm.calls.pro_epilogue_use)
> > > > +    return;
> > > > +
> > > > +  bool gpr_only = true;
> > > > +  bool used_only = true;
> > > > +  enum zero_call_used_regs zero_call_used_regs_type = zero_call_used_regs_unset;
> > > > +
> > > > +  if (flag_zero_call_used_regs)
> > > > +    if (DECL_ZERO_CALL_USED_REGS (current_function_decl)
> > > > +     == zero_call_used_regs_unset)
> > > > +      zero_call_used_regs_type = flag_zero_call_used_regs;
> > > > +    else
> > > > +      zero_call_used_regs_type
> > > > +     = DECL_ZERO_CALL_USED_REGS (current_function_decl);
> > > > +  else
> > > > +    zero_call_used_regs_type = DECL_ZERO_CALL_USED_REGS (current_function_decl);
> > > > +
> > > > +  /* No need to zero call-used-regs when no user request is present.  */
> > > > +  if (zero_call_used_regs_type <= zero_call_used_regs_skip)
> > > > +    return;
> > > > +
> > > > +  /* 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.  */
> > > > +  switch (zero_call_used_regs_type)
> > > > +    {
> > > > +      case zero_call_used_regs_all_gpr:
> > > > +     used_only = false;
> > > > +     break;
> > > > +      case zero_call_used_regs_used:
> > > > +     gpr_only = false;
> > > > +     break;
> > > > +      case zero_call_used_regs_all:
> > > > +     gpr_only = false;
> > > > +     used_only = false;
> > > > +     break;
> > > > +      default:
> > > > +     break;
> > > > +    }
> > > > +
> > > > +  /* An optimization to use a single hard insn to zero all vector registers on
> > > > +     the target that provides such insn.  */
> > > > +  if (!gpr_only
> > > > +      && targetm.calls.zero_all_vector_registers)
> > > > +    {
> > > > +      rtx zero_all_vec_insn
> > > > +     = targetm.calls.zero_all_vector_registers (used_only);
> > > > +      if (zero_all_vec_insn)
> > > > +     {
> > > > +       emit_insn (zero_all_vec_insn);
> > > > +       gpr_only = true;
> > > > +     }
> > > > +    }
> > > > +
> > > > +  /* For each of the hard registers, check to see whether we should zero it if:
> > > > +     1. it is a call-used-registers;
> > > > + and 2. it is not a fixed-registers;
> > > > + and 3. it is not live at the end of the routine;
> > > > + and 4. it is general purpose register if gpr_only is true;
> > > > + and 5. it is used in the routine if used_only is true;
> > > > +   */
> > > > +
> > > > +  /* This array holds the zero rtx with the correponding machine mode.  */
> > > > +  rtx zero_rtx[(int)MAX_MACHINE_MODE];
> > > > +  for (int i = 0; i < (int) MAX_MACHINE_MODE; i++)
> > > > +    zero_rtx[i] = NULL_RTX;
> > > > +
> > > > +  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
> > > > +    {
> > > > +      if (!this_target_hard_regs->x_call_used_regs[regno])

Use if (!call_used_regs[regno])

> > > > +     continue;
> > > > +      if (fixed_regs[regno])
> > > > +     continue;
> > > > +      if (is_live_reg_at_exit (regno))
> > > > +     continue;

How can a call-used reg be live at exit?

> > > > +      if (!targetm.calls.zero_call_used_regno_p (regno, gpr_only))
> > > > +     continue;

Why does the target need some extra say here?

> > > > +      if (used_only && !df_regs_ever_live_p (regno))

So I suppose this does not include uses by callees of this function?

> > > > +     continue;
> > > > +
> > > > +      /* Now we can emit insn to zero this register.  */
> > > > +      rtx reg, tmp;
> > > > +
> > > > +      machine_mode mode
> > > > +     = targetm.calls.zero_call_used_regno_mode (regno,
> > > > +                                                reg_raw_mode[regno]);

In what case does the target ever need to adjust this (we're dealing
with hard-regs only?)?

> > > > +      if (mode == VOIDmode)
> > > > +     continue;
> > > > +      if (!have_regs_of_mode[mode])
> > > > +     continue;

When does this happen?

> > > > +
> > > > +      reg = gen_rtx_REG (mode, regno);
> > > > +      if (zero_rtx[(int)mode] == NULL_RTX)
> > > > +     {
> > > > +       zero_rtx[(int)mode] = reg;
> > > > +       tmp = gen_rtx_SET (reg, const0_rtx);
> > > > +       emit_insn (tmp);
> > > > +     }
> > > > +      else
> > > > +     emit_move_insn (reg, zero_rtx[(int)mode]);

Not sure but I think the canonical zero to use is CONST0_RTX (mode)
but I may be wrong.  I'd rather have the target be able to specify
some special instruction for zeroing here.  Some may have
multi-reg set instructions for example.  That said, can't we
defer the actual zeroing to the target in full and only compute
a hard-reg-set of to-be zerored registers here and pass that
to a target hook?

> > > > +
> > > > +      emit_insn (targetm.calls.pro_epilogue_use (reg));
> > > > +    }
> > > > +
> > > > +  return;
> > > > +}
> > > > +
> > > > +
> > > > /* Return a sequence to be used as the epilogue for the current function,
> > > >   or NULL.  */
> > > > 
> > > > @@ -5819,6 +5961,9 @@ make_epilogue_seq (void)
> > > > 
> > > >  start_sequence ();
> > > >  emit_note (NOTE_INSN_EPILOGUE_BEG);
> > > > +
> > > > +  gen_call_used_regs_seq ();
> > > > +

The caller eventually performs shrink-wrapping - are you sure that
doesn't mess up things?

> > > >  rtx_insn *seq = targetm.gen_epilogue ();
> > > >  if (seq)
> > > >    emit_jump_insn (seq);
> > > > diff --git a/gcc/function.h b/gcc/function.h
> > > > index d55cbdd..fc36c3e 100644
> > > > --- a/gcc/function.h
> > > > +++ b/gcc/function.h
> > > > @@ -705,4 +705,6 @@ extern const char *current_function_name (void);
> > > > 
> > > > extern void used_types_insert (tree);
> > > > 
> > > > +extern bool is_live_reg_at_exit (unsigned int);
> > > > +
> > > > #endif  /* GCC_FUNCTION_H */
> > > > diff --git a/gcc/target.def b/gcc/target.def
> > > > index 07059a8..8aab63e 100644
> > > > --- a/gcc/target.def
> > > > +++ b/gcc/target.def
> > > > @@ -5022,6 +5022,26 @@ If this hook is not defined, then FUNCTION_VALUE_REGNO_P will be used.",
> > > > default_function_value_regno_p)
> > > > 
> > > > DEFHOOK
> > > > +(zero_call_used_regno_p,
> > > > + "A target hook that returns @code{true} if @var{regno} is the number of a\n\
> > > > +call used register.  If @var{general_reg_only_p} is @code{true},\n\
> > > > +@var{regno} must be the number of a hard general register.\n\
> > > > +\n\
> > > > +If this hook is not defined, then default_zero_call_used_regno_p will be used.",
> > > > + bool, (const unsigned int regno, bool general_reg_only_p),
> > > > + default_zero_call_used_regno_p)
> > > > +
> > > > +DEFHOOK
> > > > +(zero_call_used_regno_mode,
> > > > + "A target hook that returns a mode of suitable to zero the register for the\n\
> > > > +call used register @var{regno} in @var{mode}.\n\
> > > > +\n\
> > > > +If this hook is not defined, then default_zero_call_used_regno_mode will be\n\
> > > > +used.",
> > > > + machine_mode, (const unsigned int regno, machine_mode mode),
> > > > + default_zero_call_used_regno_mode)
> > > > +
> > > > +DEFHOOK
> > > > (fntype_abi,
> > > > "Return the ABI used by a function with type @var{type}; see the\n\
> > > > definition of @code{predefined_function_abi} for details of the ABI\n\
> > > > @@ -5068,6 +5088,19 @@ argument list due to stack realignment.  Return @code{NULL} if no DRAP\n\
> > > > is needed.",
> > > > rtx, (void), NULL)
> > > > 
> > > > +DEFHOOK
> > > > +(pro_epilogue_use,
> > > > + "This hook should return a UNSPEC_VOLATILE rtx to mark a register in use to\n\
> > > > +prevent deleting register setting instructions in proprologue and epilogue.",
> > > > + rtx, (rtx reg), NULL)
> > > > +
> > > > +DEFHOOK
> > > > +(zero_all_vector_registers,
> > > > + "This hook should return an rtx to zero all vector registers at function\n\
> > > > +exit.  If @var{used_only} is @code{true}, only used vector registers should\n\
> > > > +be zeroed.  Return @code{NULL} if possible",
> > > > + rtx, (bool used_only), NULL)
> > > > +
> > > > /* Return true if all function parameters should be spilled to the
> > > >   stack.  */
> > > > DEFHOOK
> > > > diff --git a/gcc/targhooks.c b/gcc/targhooks.c
> > > > index 0113c7b..ed02173 100644
> > > > --- a/gcc/targhooks.c
> > > > +++ b/gcc/targhooks.c
> > > > @@ -987,6 +987,23 @@ default_function_value_regno_p (const unsigned int regno ATTRIBUTE_UNUSED)
> > > > #endif
> > > > }
> > > > 
> > > > +/* The default hook for TARGET_ZERO_CALL_USED_REGNO_P.  */
> > > > +
> > > > +bool
> > > > +default_zero_call_used_regno_p (const unsigned int,
> > > > +                             bool)
> > > > +{
> > > > +  return false;
> > > > +}
> > > > +
> > > > +/* The default hook for TARGET_ZERO_CALL_USED_REGNO_MODE.  */
> > > > +
> > > > +machine_mode
> > > > +default_zero_call_used_regno_mode (const unsigned int, machine_mode mode)
> > > > +{
> > > > +  return mode;
> > > > +}
> > > > +
> > > > rtx
> > > > default_internal_arg_pointer (void)
> > > > {
> > > > diff --git a/gcc/targhooks.h b/gcc/targhooks.h
> > > > index b572a36..370df19 100644
> > > > --- a/gcc/targhooks.h
> > > > +++ b/gcc/targhooks.h
> > > > @@ -162,6 +162,9 @@ extern bool hook_bool_const_rtx_commutative_p (const_rtx, int);
> > > > extern rtx default_function_value (const_tree, const_tree, bool);
> > > > extern rtx default_libcall_value (machine_mode, const_rtx);
> > > > extern bool default_function_value_regno_p (const unsigned int);
> > > > +extern bool default_zero_call_used_regno_p (const unsigned int, bool);
> > > > +extern machine_mode default_zero_call_used_regno_mode (const unsigned int,
> > > > +                                                    machine_mode);
> > > > extern rtx default_internal_arg_pointer (void);
> > > > extern rtx default_static_chain (const_tree, bool);
> > > > extern void default_trampoline_init (rtx, tree, rtx);
> > > > 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..3c2ac72
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c
> > > > @@ -0,0 +1,3 @@
> > > > +/* { dg-do compile } */
> > > > +/* { dg-options "-O2 -fzero-call-used-regs=used" } */
> > > > +/* { dg-error "'-fzero-call-used-regs=' is not supported for this target" "" { target { ! "i?86-*-* x86_64-*-*" } } 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..acf48c4
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
> > > > @@ -0,0 +1,4 @@
> > > > +/* { dg-do compile { target *-*-linux* } } */
> > > > +/* { dg-options "-O2" } */
> > > > +
> > > > +extern int foo (int) __attribute__ ((zero_call_used_regs("all-gpr"))); /* { dg-warning " attribute directive ignored" "" {target { ! "i?86-*-* x86_64-*-*" } } 0 } */
> > > > 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..cbb63a4
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.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 "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-23.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c
> > > > new file mode 100644
> > > > index 0000000..7573197
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c
> > > > @@ -0,0 +1,19 @@
> > > > +/* { 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-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-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-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/toplev.c b/gcc/toplev.c
> > > > index 95eea63..01a1f24 100644
> > > > --- a/gcc/toplev.c
> > > > +++ b/gcc/toplev.c
> > > > @@ -1464,6 +1464,15 @@ process_options (void)
> > > >       }
> > > >    }
> > > > 
> > > > +  if (flag_zero_call_used_regs != zero_call_used_regs_skip
> > > > +      && !targetm.calls.pro_epilogue_use)
> > > > +    {
> > > > +      error_at (UNKNOWN_LOCATION,
> > > > +             "%<-fzero-call-used-regs=%> is not supported for this "
> > > > +             "target");
> > > > +      flag_zero_call_used_regs = zero_call_used_regs_skip;
> > > > +    }
> > > > +
> > > >  /* One region RA really helps to decrease the code size.  */
> > > >  if (flag_ira_region == IRA_REGION_AUTODETECT)
> > > >    flag_ira_region
> > > > diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> > > > index 8c5a2e3..71badbd 100644
> > > > --- a/gcc/tree-core.h
> > > > +++ b/gcc/tree-core.h
> > > > @@ -1825,7 +1825,11 @@ struct GTY(()) tree_decl_with_vis {
> > > > unsigned final : 1;
> > > > /* Belong to FUNCTION_DECL exclusively.  */
> > > > unsigned regdecl_flag : 1;
> > > > - /* 14 unused bits. */
> > > > +
> > > > + /* How to clear call-used registers upon function return.  */
> > > > + ENUM_BITFIELD(zero_call_used_regs) zero_call_used_regs_type : 3;
> > > > +
> > > > + /* 11 unused bits.  */

So instead of wasting "precious" bits please use lookup_attribute
in the single place you query this value (which is once per function).
There's no need to complicate matters by trying to maintain the above.

> > > > };
> > > > 
> > > > struct GTY(()) tree_var_decl {
> > > > diff --git a/gcc/tree.h b/gcc/tree.h
> > > > index cf546ed..d378a88 100644
> > > > --- a/gcc/tree.h
> > > > +++ b/gcc/tree.h
> > > > @@ -2925,6 +2925,11 @@ extern void decl_value_expr_insert (tree, tree);
> > > > #define DECL_VISIBILITY(NODE) \
> > > >  (DECL_WITH_VIS_CHECK (NODE)->decl_with_vis.visibility)
> > > > 
> > > > +/* Value of the function decl's type of zeroing the call used
> > > > +   registers upon return from function.  */
> > > > +#define DECL_ZERO_CALL_USED_REGS(NODE) \
> > > > +  (DECL_WITH_VIS_CHECK (NODE)->decl_with_vis.zero_call_used_regs_type)
> > > > +
> > > > /* Nonzero means that the decl (or an enclosing scope) had its
> > > >   visibility specified rather than being inferred.  */
> > > > #define DECL_VISIBILITY_SPECIFIED(NODE) \
> > > > -- 
> > > > 1.9.1
> > >
> > 
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)


More information about the Gcc-patches mailing list