Ping.

Richard and Uros,

Could you please review the patch and let me know whether it’s Okay for gcc 11?

Thanks a lot.


Qing

> On Oct 6, 2020, at 9:01 AM, Qing Zhao via Gcc-patches 
> <gcc-patches@gcc.gnu.org> wrote:
> 
> Hi, Gcc team,
> 
> This is the 3rd version of the implementation of patch -fzero-call-used-regs.
> 
> We will provide a new feature into GCC:
> 
> Add 
> -fzero-call-used-regs=[skip|used-gpr-arg|used-arg|all-arg|used-gpr|all-gpr|used|all]
>  command-line option
> and
> zero_call_used_regs("skip|used-gpr-arg|used-arg|all-arg|used-gpr|all-gpr|used|all")
>  function attribues:
> 
>   1. -fzero-call-used-regs=skip and zero_call_used_regs("skip")
> 
>   Don't zero call-used registers upon function return. This is the default 
> behavior.
> 
>   2. -fzero-call-used-regs=used-gpr-arg and 
> zero_call_used_regs("used-gpr-arg")
> 
>   Zero used call-used general purpose registers that are used to pass 
> parameters upon function return.
> 
>   3. -fzero-call-used-regs=used-arg and zero_call_used_regs("used-arg")
> 
>   Zero used call-used registers that are used to pass parameters upon 
> function return.
> 
>   4. -fzero-call-used-regs=all-arg and zero_call_used_regs("all-arg")
> 
>   Zero all call-used registers that are used to pass parameters upon function 
> return.
> 
>   5. -fzero-call-used-regs=used-gpr and zero_call_used_regs("used-gpr")
> 
>   Zero used call-used general purpose registers upon function return.
> 
>   6. -fzero-call-used-regs=all-gpr and zero_call_used_regs("all-gpr")
> 
>   Zero all call-used general purpose registers upon function return.
> 
>   7. -fzero-call-used-regs=used and zero_call_used_regs("used")
> 
>   Zero used call-used registers upon function return.
> 
>   8. -fzero-call-used-regs=all and zero_call_used_regs("all")
> 
>   Zero all call-used registers upon function return.
> 
> 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.
> 
> {skip}, which is the default, doesn't zero call-used registers.
> 
> {used-arg-gpr} zeros used call-used general purpose registers that
> pass parameters. {used-arg} zeros used call-used registers that
> pass parameters. {arg} zeros all call-used registers that pass
> parameters. These 3 choices are used for ROP mitigation.
> 
> {used-gpr} zeros call-used general purpose registers
> which are used in function.  {all-gpr} zeros all
> call-used registers.  {used} zeros call-used registers which
> are used in function.  {all} zeros all call-used registers.
> These 4 choices are used for preventing information leak through
> registers.
> 
> You can control this behavior for a specific function by using the function
> attribute {zero_call_used_regs}.
> 
> ******Tests be done:
> 1. Gcc bootstrap on x86, aarch64 and rs6000.
> 2. Regression test on x86, aarch64 and rs6000.
> (X86, aarch64 have no any issue, rs6000 failed at the new testing case in 
> middle end which is expected)
> 
> 3. Cpu2017 on x86, -O2 
> -fzero-call-used-regs=used-gpr-arg|used-arg|all-arg|used-gpr|all-gpr|used|all
> 
> ******runtime performance data of CPU2017 on x86
> https://urldefense.com/v3/__https://gitlab.com/x86-gcc/gcc/-/wikis/uploads/e9c5bedba6e387586364571f2eae3b8d/zero_call_used_regs_runtime_New.csv__;!!GqivPVa7Brio!KabFawXf4waV8v6RfHHKO5ZoFbei3YVhbR9Pquv8AVICgBHpjAkuEb5mfVbaNozS$
>   
> <https://urldefense.com/v3/__https://gitlab.com/x86-gcc/gcc/-/wikis/uploads/e9c5bedba6e387586364571f2eae3b8d/zero_call_used_regs_runtime_New.csv__;!!GqivPVa7Brio!KabFawXf4waV8v6RfHHKO5ZoFbei3YVhbR9Pquv8AVICgBHpjAkuEb5mfVbaNozS$
>  >
> 
> ******The major changes compared to the previous version are:
> 
> 1. Add 3 new sub-options and corresponding function attributes:
>  used-gpr-arg, used-arg, all-arg
>  for ROP mitigation purpose;
> 2. Updated user manual;
> 3. Re-design of the implementation:
> 
>  3.1 data flow change to reflect the newly added zeroing insns to avoid
>  these insns been deleted, moved, or merged by later passes:
> 
>  3.1.1.
>  abstract EPILOGUE_USES into a new target-independent wrapper function that
>  (a) returns true if EPILOGUE_USES itself returns true and (b) returns
>  true for registers that need to be zero on return, if the zeroing
>  instructions have already been inserted.  The places that currently
>  test EPILOGUE_USES should then test this new wrapper function instead.
> 
>  Add this new wrapper function to df.h and df-scan.c.
> 
>  3.1.2.
>  add a new utility routine "expand_asm_reg_clobber_mem_blockage" to generate
>  a volatile asm insn that clobbers all the hard registers that are zeroed.
> 
>  emit this volatile asm in the very beginning of the zeroing sequence.
> 
>  3.2 new pass:
>  add a new pass in the beginning of "late_compilation", before
>  "pass_compute_alignment", called "pass_zero_call_used_regs".
> 
>  in this new pass,
>  * compute the data flow information; (df_analyze ());
>  * scan the exit block from backward to look for "return":
>    A. for each return, compute the "need_zeroed_hardregs" based on
>    the user request, and data flow information, and function ABI info.
>    B. pass this need_zeroed_hardregs set to target hook "zero_call_used_regs"
>    to generate the instruction sequnce that zero the regs.
>    C. Data flow maintenance. 
> 4.Use "lookup_attribute" to get the attribute information instead of setting
>  the attribute information into "tree_decl_with_vis" in tree-core.h.
> 
> ******The changelog:
> 
> gcc/ChangeLog: 
> 2020-10-05  Qing Zhao  <qing.z...@oracle.com <mailto:qing.z...@oracle.com>>
>           H.J. Lu  <hjl.to...@gmail.com <mailto:hjl.to...@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_mm_registers): Likewise.
>       (ix86_zero_call_used_regs): Likewise.
>       (TARGET_ZERO_CALL_USED_REGS): Define.
>       * coretypes.h (enum zero_call_used_regs): New type.
>       * 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 field zeroed_reg_set.
>       * function.c (is_live_reg_at_return): New function.
>       (gen_call_used_regs_seq): Likewise.
>       (rest_of_zero_call_used_regs): Likewise.
>       (class pass_zero_call_used_regs): New class.
>       (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.
>       * 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-05  Qing Zhao  <qing.z...@oracle.com <mailto:qing.z...@oracle.com>>
>           H.J. Lu  <hjl.to...@gmail.com <mailto:hjl.to...@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-05  Qing Zhao  <qing.z...@oracle.com <mailto:qing.z...@oracle.com>>
>           H.J. Lu  <hjl.to...@gmail.com <mailto:hjl.to...@gmail.com>>
> 
>       * c-c++-common/zero-scratch-regs-1.c: New test.
>       * c-c++-common/zero-scratch-regs-2.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-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:
> 
> ---
> gcc/c-family/c-attribs.c                           |  50 +++++
> gcc/common.opt                                     |  32 +++
> gcc/config/i386/i386.c                             | 158 +++++++++++++
> gcc/coretypes.h                                    |  13 ++
> gcc/df-scan.c                                      |  12 +-
> gcc/df.h                                           |   1 +
> gcc/doc/extend.texi                                |  24 ++
> gcc/doc/invoke.texi                                |  25 ++-
> gcc/doc/tm.texi                                    |  10 +
> gcc/doc/tm.texi.in                                 |   2 +
> gcc/emit-rtl.h                                     |   3 +
> gcc/function.c                                     | 247 ++++++++++++++++++++-
> gcc/optabs.c                                       |  43 ++++
> gcc/optabs.h                                       |   2 +
> gcc/passes.def                                     |   1 +
> gcc/recog.c                                        |  15 ++
> gcc/recog.h                                        |   1 +
> gcc/resource.c                                     |   2 +-
> gcc/target.def                                     |  13 ++
> gcc/targhooks.c                                    |  35 +++
> gcc/targhooks.h                                    |   1 +
> gcc/testsuite/c-c++-common/zero-scratch-regs-1.c   |  15 ++
> gcc/testsuite/c-c++-common/zero-scratch-regs-2.c   |  16 ++
> .../gcc.target/i386/zero-scratch-regs-1.c          |  12 +
> .../gcc.target/i386/zero-scratch-regs-10.c         |  21 ++
> .../gcc.target/i386/zero-scratch-regs-11.c         |  39 ++++
> .../gcc.target/i386/zero-scratch-regs-12.c         |  39 ++++
> .../gcc.target/i386/zero-scratch-regs-13.c         |  21 ++
> .../gcc.target/i386/zero-scratch-regs-14.c         |  19 ++
> .../gcc.target/i386/zero-scratch-regs-15.c         |  14 ++
> .../gcc.target/i386/zero-scratch-regs-16.c         |  14 ++
> .../gcc.target/i386/zero-scratch-regs-17.c         |  13 ++
> .../gcc.target/i386/zero-scratch-regs-18.c         |  13 ++
> .../gcc.target/i386/zero-scratch-regs-19.c         |  12 +
> .../gcc.target/i386/zero-scratch-regs-2.c          |  19 ++
> .../gcc.target/i386/zero-scratch-regs-20.c         |  23 ++
> .../gcc.target/i386/zero-scratch-regs-21.c         |  14 ++
> .../gcc.target/i386/zero-scratch-regs-22.c         |  20 ++
> .../gcc.target/i386/zero-scratch-regs-23.c         |  28 +++
> .../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-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/tree-pass.h                                    |   1 +
> 50 files changed, 1187 insertions(+), 6 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-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-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 c779d13..69c3886 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,52 @@ 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);
> +
> +  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;
> +    }
> +
> +  if (TREE_CODE (id) != STRING_CST)
> +    {
> +      error ("attribute %qE arguments not a string", name);
> +      *no_add_attris = true;
> +      return NULL_TREE;
> +    }
> +
> +  if ((strcmp (TREE_STRING_POINTER (id), "skip") != 0)
> +      && (strcmp (TREE_STRING_POINTER (id), "used-gpr-arg") != 0)
> +      && (strcmp (TREE_STRING_POINTER (id), "used-arg") != 0)
> +      && (strcmp (TREE_STRING_POINTER (id), "all-arg") != 0)
> +      && (strcmp (TREE_STRING_POINTER (id), "used-gpr") != 0)
> +      && (strcmp (TREE_STRING_POINTER (id), "all-gpr") != 0)
> +      && (strcmp (TREE_STRING_POINTER (id), "used") != 0)
> +      && (strcmp (TREE_STRING_POINTER (id), "all") != 0))
> +    {
> +      error ("attribute %qE argument must be one of %qs, %qs, %qs, %qs,"
> +          "%qs, %qs, %qs, or %qs",
> +          name, "skip", "used-gpr-arg", "used-arg", "all-arg",
> +          "used-gpr", "all-gpr", "used", "all");
> +      *no_add_attris = true;
> +      return NULL_TREE;
> +    }
> +
> +  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..50bbf9c 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -3111,6 +3111,38 @@ 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_unset)
> +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-arg) 
> Value(zero_call_used_regs_used_gpr_arg)
> +
> +EnumValue
> +Enum(zero_call_used_regs) String(used-arg) 
> Value(zero_call_used_regs_used_arg)
> +
> +EnumValue
> +Enum(zero_call_used_regs) String(all-arg) Value(zero_call_used_regs_all_arg)
> +
> +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 f684954..620114f 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -3551,6 +3551,161 @@ 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.
> +   Stack registers (st0-st7) and mm0-mm7 are aliased with each other.
> +   very hard to be zeroed individually, don't zero individual st or
> +   mm registgers at this time.  */
> +
> +static bool
> +zero_call_used_regno_p (const unsigned int regno,
> +                     bool all_sse_zeroed)
> +{
> +  return GENERAL_REGNO_P (regno)
> +      || (!all_sse_zeroed && SSE_REGNO_P (regno))
> +      || MASK_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
> +    return HImode;
> +}
> +
> +/* Generate a rtx to zero all vector registers togetehr 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 a rtx to zero all st and mm registers togetehr if possible,
> +   otherwise, return NULL.  */
> +
> +static rtx
> +zero_all_st_mm_registers (HARD_REG_SET need_zeroed_hardregs)
> +{
> +  if (!TARGET_MMX)
> +    return NULL;
> +
> +  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))
> +      return NULL;
> +
> +  return gen_mmx_emms ();
> +}
> +
> +/* 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;
> +
> +  /* 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;
> +    }
> +
> +  /* then, let's see whether we can zero all st+mm registers togeter.  */
> +  rtx zero_all_st_mm_insn = zero_all_st_mm_registers (need_zeroed_hardregs);
> +  if (zero_all_st_mm_insn)
> +    emit_insn (zero_all_st_mm_insn);
> +
> +  /* Now, generate instructions to zero all the registers.  */
> +
> +  CLEAR_HARD_REG_SET (zeroed_hardregs);
> +  rtx zero_gpr = NULL_RTX;
> +  rtx zero_vector = NULL_RTX;
> +  rtx zero_mask = 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))
> +     continue;
> +
> +      SET_HARD_REG_BIT (zeroed_hardregs, regno);
> +
> +      rtx reg, tmp;
> +      machine_mode mode = zero_call_used_regno_mode (regno);
> +
> +      reg = gen_rtx_REG (mode, regno);
> +
> +      if (mode == SImode)
> +     if (zero_gpr == NULL_RTX)
> +       {
> +         zero_gpr = reg;
> +         tmp = gen_rtx_SET (reg, const0_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, const0_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, const0_rtx);
> +         emit_insn (tmp);
> +       }
> +     else
> +       emit_move_insn (reg, zero_mask);
> +      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 +23384,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/coretypes.h b/gcc/coretypes.h
> index 6b6cfcd..0ce5eb4 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -418,6 +418,19 @@ 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_arg,
> +  zero_call_used_regs_used_arg,
> +  zero_call_used_regs_all_arg,
> +  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/df-scan.c b/gcc/df-scan.c
> index 93b060f..630970b 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->zeroed_reg_set, 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..f56f61a 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -3992,6 +3992,30 @@ 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}.
> +This is used to increase the program security by either mitigating
> +Return-Oriented Programming (ROP) or preventing information leak
> +through registers.
> +@samp{skip} doesn't zero call-used registers.
> +
> +@samp{used-arg-gpr} zeros used call-used general purpose registers that
> +pass parameters. @samp{used-arg} zeros used call-used registers that
> +pass parameters. @samp{arg} zeros all call-used registers that pass
> +parameters.  These 3 choices are used for ROP mitigation.
> +
> +@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.
> +These 4 choices are used for preventing information leak through
> +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 c049932..aa04a3c 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,29 @@ 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.
> +
> +@samp{skip}, which is the default, doesn't zero call-used registers.
> +
> +@samp{used-gpr-arg} zeros used call-used general purpose registers that
> +pass parameters. @samp{used-arg} zeros used call-used registers that
> +pass parameters. @samp{all-arg} zeros all call-used registers that pass
> +parameters.  These 3 choices are used for ROP mitigation.
> +
> +@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.
> +These 4 choices are used for preventing information leak through
> +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 97437e8..7ecff05 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -12053,6 +12053,16 @@ 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{need_zeroed_hardregs})
> +This target hook emits instructions to zero registers specified
> +by @var{need_zeroed_hardregs} at function return, at the same time
> +return the hard register set that are actually zeroed by the hook
> +Define this hook if the target has more effecient instructions to
> +zero call-used registers, or if the target only tries to zero a subset
> +of @var{need_zeroed_hardregs}.
> +If the hook is not defined, the default_zero_call_used_reg will be used.
> +@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..2dbeace0 100644
> --- a/gcc/emit-rtl.h
> +++ b/gcc/emit-rtl.h
> @@ -310,6 +310,9 @@ struct GTY(()) rtl_data {
>     sets them.  */
>  HARD_REG_SET asm_clobbers;
> 
> +  /* All hard registers that are zeroed at the return of the routine.  */
> +  HARD_REG_SET zeroed_reg_set;
> +
>  /* The highest address seen during shorten_branches.  */
>  int max_insn_address;
> };
> diff --git a/gcc/function.c b/gcc/function.c
> index c612959..c8181bd 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"
> @@ -5815,6 +5816,182 @@ make_prologue_seq (void)
>  return seq;
> }
> 
> +/* Check whether the hard register REGNO is live before the return insn RET. 
>  */
> +static bool
> +is_live_reg_at_return (unsigned int regno, rtx_insn * ret)
> +{
> +  basic_block bb = BLOCK_FOR_INSN (ret);
> +  auto_bitmap live_out;
> +  bitmap_copy (live_out, df_get_live_out (bb));
> +  df_simulate_one_insn_backwards (bb, ret, live_out);
> +
> +  if (REGNO_REG_SET_P (live_out, regno))
> +    return true;
> +
> +  return false;
> +}
> +
> +/* Emit a sequence of insns to zero the call-used-registers before RET.  */
> +
> +static void
> +gen_call_used_regs_seq (rtx_insn *ret)
> +{
> +  bool gpr_only = true;
> +  bool used_only = true;
> +  bool arg_only = true;
> +  enum zero_call_used_regs zero_regs_type = zero_call_used_regs_unset;
> +  enum zero_call_used_regs attr_zero_regs_type
> +                         = zero_call_used_regs_unset;
> +  tree attr_zero_regs
> +     = lookup_attribute ("zero_call_used_regs",
> +                         DECL_ATTRIBUTES (cfun->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);
> +
> +      if (strcmp (TREE_STRING_POINTER (attr_zero_regs), "skip") == 0)
> +     attr_zero_regs_type = zero_call_used_regs_skip;
> +      else if (strcmp (TREE_STRING_POINTER (attr_zero_regs), "used-gpr-arg")
> +             == 0)
> +     attr_zero_regs_type = zero_call_used_regs_used_gpr_arg;
> +      else if (strcmp (TREE_STRING_POINTER (attr_zero_regs), "used-arg") == 
> 0)
> +     attr_zero_regs_type = zero_call_used_regs_used_arg;
> +      else if (strcmp (TREE_STRING_POINTER (attr_zero_regs), "all-arg") == 0)
> +     attr_zero_regs_type = zero_call_used_regs_all_arg;
> +      else if (strcmp (TREE_STRING_POINTER (attr_zero_regs), "used-gpr") == 
> 0)
> +     attr_zero_regs_type = zero_call_used_regs_used_gpr;
> +      else if (strcmp (TREE_STRING_POINTER (attr_zero_regs), "all-gpr") == 0)
> +     attr_zero_regs_type = zero_call_used_regs_all_gpr;
> +      else if (strcmp (TREE_STRING_POINTER (attr_zero_regs), "used") == 0)
> +     attr_zero_regs_type = zero_call_used_regs_used;
> +      else if (strcmp (TREE_STRING_POINTER (attr_zero_regs), "all") == 0)
> +     attr_zero_regs_type = zero_call_used_regs_all;
> +      else
> +     gcc_assert (0);
> +    }
> +
> +  if (flag_zero_call_used_regs)
> +    if (!attr_zero_regs)
> +      zero_regs_type = flag_zero_call_used_regs;
> +    else
> +      zero_regs_type = attr_zero_regs_type;
> +  else
> +    zero_regs_type = attr_zero_regs_type;
> +
> +  /* No need to zero call-used-regs when no user request is present.  */
> +  if (zero_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_regs_type)
> +    {
> +      case zero_call_used_regs_used_arg:
> +     gpr_only = false;
> +     break;
> +      case zero_call_used_regs_all_arg:
> +     gpr_only = false;
> +     used_only = false;
> +     break;
> +      case zero_call_used_regs_used_gpr:
> +     arg_only = false;
> +     break;
> +      case zero_call_used_regs_all_gpr:
> +     used_only = false;
> +     arg_only = false;
> +     break;
> +      case zero_call_used_regs_used:
> +     gpr_only = false;
> +     arg_only = false;
> +     break;
> +      case zero_call_used_regs_all:
> +     gpr_only = false;
> +     used_only = false;
> +     arg_only = false;
> +     break;
> +      default:
> +     break;
> +    }
> +
> +  /* 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 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;
> +   */
> +
> +  HARD_REG_SET need_zeroed_hardregs;
> +  CLEAR_HARD_REG_SET (need_zeroed_hardregs);
> +  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
> +    {
> +      if (!this_target_hard_regs->x_call_used_regs[regno])
> +     continue;
> +      if (fixed_regs[regno])
> +     continue;
> +      if (is_live_reg_at_return (regno, ret))
> +     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 (need_zeroed_hardregs, regno);
> +    }
> +
> +  if (hard_reg_set_empty_p (need_zeroed_hardregs))
> +    return;
> +
> +  /* Now we get a hard register set that need 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 (need_zeroed_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->zeroed_reg_set |= zeroed_hardregs;
> +      df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun));
> +    }
> +  return;
> +}
> +
> +
> /* Return a sequence to be used as the epilogue for the current function,
>   or NULL.  */
> 
> @@ -6486,7 +6663,75 @@ make_pass_thread_prologue_and_epilogue (gcc::context 
> *ctxt)
> {
>  return new pass_thread_prologue_and_epilogue (ctxt);
> }
> -
> 
> +
> +static unsigned int
> +rest_of_zero_call_used_regs (void)
> +{
> +  basic_block bb;
> +  rtx_insn *insn;
> +
> +  /* This pass needs data flow information.  */
> +  df_analyze ();
> +
> +  /* Search all the "return"s in the routine, and insert instruction 
> sequence to
> +     zero the call used registers.  */
> +  FOR_EACH_BB_REVERSE_FN (bb, cfun)
> +    if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun)
> +     || (single_succ_p (bb)
> +         && single_succ (bb) == EXIT_BLOCK_PTR_FOR_FN (cfun)))
> +      FOR_BB_INSNS_REVERSE (bb, insn)
> +     if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn)))
> +       {
> +         /* Now we can insert the instruction sequence to zero the call used
> +            registers before this insn.  */
> +         gen_call_used_regs_seq (insn);
> +         break;
> +       }
> +
> +  return 0;
> +}
> +
> +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 bool gate (function *)
> +    {
> +      return flag_zero_call_used_regs > zero_call_used_regs_unset;
> +    }
> +  virtual unsigned int execute (function *)
> +    {
> +      return rest_of_zero_call_used_regs ();
> +    }
> +
> +}; // class pass_zero_call_used_regs
> +
> +} // 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..57e5c5d 100644
> --- a/gcc/optabs.c
> +++ b/gcc/optabs.c
> @@ -6484,6 +6484,49 @@ 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 ZEROED_REGS.  */
> +
> +void
> +expand_asm_reg_clobber_mem_blockage (HARD_REG_SET zeroed_regs)
> +{
> +  rtx asm_op, clob_mem, clob_reg;
> +
> +  unsigned int num_of_regs = 0;
> +  for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
> +    if (TEST_HARD_REG_BIT (zeroed_regs, i))
> +      num_of_regs++;
> +
> +  if (num_of_regs == 0)
> +    return;
> +
> +  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;
> +
> +  unsigned int j = 2;
> +  for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
> +    if (TEST_HARD_REG_BIT (zeroed_regs, i))
> +      {
> +     clob_reg  = gen_rtx_CLOBBER (VOIDmode, regno_reg_rtx[i]);
> +     RTVEC_ELT (v,j) = clob_reg;
> +     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/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..472c2dc 100644
> --- a/gcc/recog.c
> +++ b/gcc/recog.c
> @@ -923,6 +923,21 @@ validate_simplify_insn (rtx_insn *insn)
>  return ((num_changes_pending () > 0) && (apply_change_group () > 0));
> }
> 
> 
> +
> +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..7d6807d 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -5080,6 +5080,19 @@ 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 registers specified\n\
> +by @var{need_zeroed_hardregs} at function return, at the same time\n\
> +return the hard register set that are actually zeroed by the hook\n\
> +Define this hook if the target has more effecient instructions to\n\
> +zero call-used registers, or if the target only tries to zero a subset\n\
> +of @var{need_zeroed_hardregs}.\n\
> +If the hook is not defined, the default_zero_call_used_reg will be used.",
> + HARD_REG_SET, (HARD_REG_SET need_zeroed_hardregs),
> +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..2318c324 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,38 @@ 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)
> +{
> +  HARD_REG_SET zeroed_hardregs;
> +  gcc_assert (!hard_reg_set_empty_p (need_zeroed_hardregs));
> +
> +  CLEAR_HARD_REG_SET (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 ("-fzero-call-used-regs not supported on this target");
> +           }
> +         delete_insns_since (last_insn);
> +       }
> +     else
> +       SET_HARD_REG_BIT (zeroed_hardregs, regno);
> +      }
> +  return 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..f44add9
> --- /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=all" } */
> +
> +volatile int result = 0;
> +int 
> +__attribute__((noinline))
> +foo (int x)
> +{
> +  return x;
> +}
> +int main()
> +{
> +  result = foo (2);
> +  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..7c8350b
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c
> @@ -0,0 +1,16 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +volatile int result = 0;
> +int 
> +__attribute__((noinline))
> +__attribute__ ((zero_call_used_regs("all")))
> +foo (int x)
> +{
> +  return x;
> +}
> +int main()
> +{
> +  result = foo (2);
> +  return 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..76742bb
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c
> @@ -0,0 +1,20 @@
> +/* { 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 "emms" } } */
> +/* { 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..18a5ffb
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c
> @@ -0,0 +1,28 @@
> +/* { 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 "emms" } } */
> +/* { 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-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/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);
> -- 
> 1.8.3.1

Reply via email to