On Sat, Oct 24, 2020 at 6:05 PM Qing Zhao <qing.z...@oracle.com> wrote:
>
> Hi,
>
> This is the 4th version of the implementation of patch -fzero-call-used-regs.
>
> The major change compared to the previous version are:
>
> 1.  Documentation change per Richard’s suggestion;
> 2.  Command sub options handling per Richard’s suggestion;
> 3.  I386 part, clearing ST registers per Uros’s suggestion;
> 4. Some minor spelling and style fix.
>
> I have tested this new GCC on both x86 and arm64, no regression.
>
> Please let me know whether it’s ready for stage 1 gcc11?
>
> Thanks.
>
>
> ******The changelog:
>
> gcc/ChangeLog:
>
> 2020-10-24  Qing Zhao  <qing.z...@oracle.com>
>             H.J.Lu  <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.
>         * df-scan.c (df_epilogue_uses_p): New function.
>         (df_get_exit_block_use_set): Replace EPILOGUE_USES with
>         df_epilogue_uses_p.
>         * df.h (df_epilogue_uses_p): Declare.
>         * doc/extend.texi: Document the new zero_call_used_regs attribute.
>         * doc/invoke.texi: Document the new -fzero-call-used-regs option.
>         * doc/tm.texi: Regenerate.
>         * doc/tm.texi.in (TARGET_ZERO_CALL_USED_REGS): New hook.
>         * emit-rtl.h (struct rtl_data): New fields zero_call_used_regs
>         and must_be_zero_on_return.
>         * flag-types.h (enum  zero_call_used_regs_code): New type.
>         * function.c (gen_call_used_regs_seq): New function.
>         (rest_of_zero_call_used_regs): Likewise.
>         (class pass_zero_call_used_regs): New class.
>         (pass_zero_call_used_regs::gate): New function.
>         (make_pass_zero_call_used_regs): New function.
>         * optabs.c (expand_asm_reg_clobber_mem_blockage): New function.
>         * optabs.h (expand_asm_reg_clobber_mem_blockage): Declare.
>         * opts.c (zero_call_used_regs_opts): New structure array
>         initialization.
>         (parse_zero_call_used_regs_options): New function.
>         (common_handle_option): Handle fzero_call_used_regs_.
>         * opts.h (zero_call_used_regs_opts): New structure array.
>         * passes.def: Add new pass pass_zero_call_used_regs.
>         * 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-24  Qing Zhao  <qing.z...@oracle.com>
>             H.J.Lu  <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-24  Qing Zhao  <qing.z...@oracle.com>
>             H.J.Lu  <hjl.to...@gmail.com>
>
>         * gcc.target/i386/zero-scratch-regs-1.c: New test.
>         * gcc.target/i386/zero-scratch-regs-10.c: New test.
>         * gcc.target/i386/zero-scratch-regs-11.c: New test.
>         * gcc.target/i386/zero-scratch-regs-12.c: New test.
>         * gcc.target/i386/zero-scratch-regs-13.c: New test.
>         * gcc.target/i386/zero-scratch-regs-14.c: New test.
>         * gcc.target/i386/zero-scratch-regs-15.c: New test.
>         * gcc.target/i386/zero-scratch-regs-16.c: New test.
>         * gcc.target/i386/zero-scratch-regs-17.c: New test.
>         * gcc.target/i386/zero-scratch-regs-18.c: New test.
>         * gcc.target/i386/zero-scratch-regs-19.c: New test.
>         * gcc.target/i386/zero-scratch-regs-2.c: New test.
>         * gcc.target/i386/zero-scratch-regs-20.c: New test.
>         * gcc.target/i386/zero-scratch-regs-21.c: New test.
>         * gcc.target/i386/zero-scratch-regs-22.c: New test.
>         * gcc.target/i386/zero-scratch-regs-23.c: New test.
>         * gcc.target/i386/zero-scratch-regs-24.c: New test.
>         * gcc.target/i386/zero-scratch-regs-25.c: New test.
>         * gcc.target/i386/zero-scratch-regs-26.c: New test.
>         * gcc.target/i386/zero-scratch-regs-27.c: New test.
>         * gcc.target/i386/zero-scratch-regs-3.c: New test.
>         * gcc.target/i386/zero-scratch-regs-4.c: New test.
>         * gcc.target/i386/zero-scratch-regs-5.c: New test.
>         * gcc.target/i386/zero-scratch-regs-6.c: New test.
>         * gcc.target/i386/zero-scratch-regs-7.c: New test.
>         * gcc.target/i386/zero-scratch-regs-8.c: New test.
>         * gcc.target/i386/zero-scratch-regs-9.c: New test.
>
> ******the patch:
>
> From 0da0f0f2203b1e3bb2be8c12a0a1addd3c20a534 Mon Sep 17 00:00:00 2001
> From: qing zhao <qing.z...@oracle.com>
> Date: Thu, 1 Oct 2020 00:27:57 +0000
> Subject: [PATCH] The 4th version of -fzero-call-used-regs
> We will provide a new feature into GCC:
>
> Add -fzero-call-used-regs=[skip|used-gpr-arg|used-arg|all-gpr-arg
>                            |all-arg|used-gpr|all-gpr|used|all]
> command-line option
> and
> zero_call_used_regs("skip|used-gpr-arg|used-arg|all-gpr-arg
>                      |all-arg|used-gpr|all-gpr|used|all")
> function attribues:
>
>   1. -fzero-call-used-regs=skip and zero_call_used_regs("skip")
>   2. -fzero-call-used-regs=used-gpr-arg and 
> zero_call_used_regs("used-gpr-arg")
>   3. -fzero-call-used-regs=used-arg and zero_call_used_regs("used-arg")
>   4. -fzero-call-used-regs=all-gpr-arg and zero_call_used_regs("all-gpr-arg")
>   5. -fzero-call-used-regs=all-arg and zero_call_used_regs("all-arg")
>   6. -fzero-call-used-regs=used-gpr and zero_call_used_regs("used-gpr")
>   7. -fzero-call-used-regs=all-gpr and zero_call_used_regs("all-gpr")
>   8. -fzero-call-used-regs=used and zero_call_used_regs("used")
>   9. -fzero-call-used-regs=all and zero_call_used_regs("all")
>
> ---
>  gcc/c-family/c-attribs.c                           |  31 +++
>  gcc/common.opt                                     |   8 +
>  gcc/config/i386/i386.c                             | 186 ++++++++++++++++++
>  gcc/df-scan.c                                      |  12 +-
>  gcc/df.h                                           |   1 +
>  gcc/doc/extend.texi                                |  43 +++++
>  gcc/doc/invoke.texi                                |  42 +++-
>  gcc/doc/tm.texi                                    |  12 ++
>  gcc/doc/tm.texi.in                                 |   2 +
>  gcc/emit-rtl.h                                     |   6 +
>  gcc/flag-types.h                                   |   9 +
>  gcc/function.c                                     | 213 
> ++++++++++++++++++++-
>  gcc/optabs.c                                       |  42 ++++
>  gcc/optabs.h                                       |   2 +
>  gcc/opts.c                                         |  47 +++++
>  gcc/opts.h                                         |   6 +
>  gcc/passes.def                                     |   1 +
>  gcc/recog.c                                        |  16 ++
>  gcc/recog.h                                        |   1 +
>  gcc/resource.c                                     |   2 +-
>  gcc/target.def                                     |  15 ++
>  gcc/targhooks.c                                    |  32 ++++
>  gcc/targhooks.h                                    |   1 +
>  gcc/testsuite/c-c++-common/zero-scratch-regs-1.c   |  15 ++
>  gcc/testsuite/c-c++-common/zero-scratch-regs-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         |  21 ++
>  .../gcc.target/i386/zero-scratch-regs-23.c         |  29 +++
>  .../gcc.target/i386/zero-scratch-regs-24.c         |  10 +
>  .../gcc.target/i386/zero-scratch-regs-25.c         |  10 +
>  .../gcc.target/i386/zero-scratch-regs-26.c         |  23 +++
>  .../gcc.target/i386/zero-scratch-regs-27.c         |  15 ++
>  .../gcc.target/i386/zero-scratch-regs-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 +
>  53 files changed, 1244 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-27.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..979b6a7 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,33 @@ handle_no_split_stack_attribute (tree *node, tree name,
>    return NULL_TREE;
>  }
>
> +/* Handle a "zero_call_used_regs" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +static tree
> +handle_zero_call_used_regs_attribute (tree *node, tree name, tree args,
> +                                     int ARG_UNUSED (flags),
> +                                     bool *no_add_attrs)
> +{
> +  tree decl = *node;
> +  tree id = TREE_VALUE (args);
> +
> +  if (TREE_CODE (decl) != FUNCTION_DECL)
> +    {
> +      error_at (DECL_SOURCE_LOCATION (decl),
> +               "%qE attribute applies only to functions", name);
> +      *no_add_attrs = true;
> +    }
> +
> +  if (TREE_CODE (id) != STRING_CST)
> +    {
> +      error ("attribute %qE arguments not a string", name);
> +      *no_add_attrs = true;
> +    }
> +
> +  return NULL_TREE;
> +}
> +
>  /* Handle a "returns_nonnull" attribute; arguments as in
>     struct attribute_spec.handler.  */
>
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 292c2de..4a13f32 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -228,6 +228,10 @@ unsigned int flag_sanitize_coverage
>  Variable
>  bool dump_base_name_prefixed = false
>
> +; What subset of registers should be zeroed
> +Variable
> +unsigned int flag_zero_call_used_regs
> +
>  ###
>  Driver
>
> @@ -3111,6 +3115,10 @@ fzero-initialized-in-bss
>  Common Report Var(flag_zero_initialized_in_bss) Init(1)
>  Put zero initialized data in the bss section.
>
> +fzero-call-used-regs=
> +Common Report RejectNegative Joined
> +Clear call-used registers upon function return.
> +
>  g
>  Common Driver RejectNegative JoinedOrMissing
>  Generate debug information in default format.
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index f684954..e66dcf0 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -3551,6 +3551,189 @@ 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.  */
> +
> +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 together if possible,
> +   otherwise, return NULL.  */
> +
> +static rtx
> +zero_all_vector_registers (HARD_REG_SET need_zeroed_hardregs)
> +{
> +  if (!TARGET_AVX)
> +    return NULL;
> +
> +  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
> +    if ((IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG)
> +        || (TARGET_64BIT
> +            && (REX_SSE_REGNO_P (regno)
> +                || (TARGET_AVX512F && EXT_REX_SSE_REGNO_P (regno)))))
> +       && !TEST_HARD_REG_BIT (need_zeroed_hardregs, regno))
> +      return NULL;
> +
> +  return gen_avx_vzeroall ();
> +}
> +
> +/* Generate insns to zero all st/mm registers together.
> +   Return true when zeroing instructions are generated.
> +   Assume the number of st registers that are zeroed is num_of_st,
> +   we will emit the following sequence to zero them together:
> +                 fldz;         \
> +                 fldz;         \
> +                 ...
> +                 fldz;         \
> +                 fstp %%st(0); \
> +                 fstp %%st(0); \
> +                 ...
> +                 fstp %%st(0);
> +   i.e., num_of_st fldz followed by num_of_st fstp to clear the stack
> +   mark stack slots empty.  */
> +
> +static bool
> +zero_all_st_mm_registers (HARD_REG_SET need_zeroed_hardregs)
> +{
> +  unsigned int num_of_st = 0;
> +  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
> +    if (STACK_REGNO_P (regno)
> +       && TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)
> +       /* When the corresponding mm register also need to be cleared too.  */
> +       && TEST_HARD_REG_BIT (need_zeroed_hardregs,
> +                             (regno - FIRST_STACK_REG + FIRST_MMX_REG)))
> +      num_of_st++;

I don't think the above logic is correct. It should go like this:

- If the function is returning an MMX register, then the function
exits in MMX mode, and MMX registers should be cleared in the same way
as XMM registers. Otherwise the ABI specifies that the function exits
in x87 mode and x87 stack should be cleared (but see below).

- There is no direct mapping of stack registers to hard register
numbers. If a stack register is used, we don't know where in the stack
the value remains. So, if _any_ stack register is touched, the whole
stack should be cleared (value, returning in x87 stack register should
obviously be excluded).

- There is no x87 argument register. 32bit targets use MMX0-3 argument
registers and return value in the XMM register. Please also note that
complex values take two stack slots in x87 stack.

Uros.

> +
> +  if (num_of_st == 0)
> +    return false;
> +
> +  rtx st_reg = gen_rtx_REG (XFmode, FIRST_STACK_REG);
> +  for (unsigned int i = 0; i < num_of_st; i++)
> +    emit_insn (gen_rtx_SET (st_reg, CONST0_RTX (XFmode)));
> +
> +  for (unsigned int i = 0; i < num_of_st; i++)
> +    {
> +      rtx insn;
> +      insn = emit_insn (gen_rtx_SET (st_reg, st_reg));
> +      add_reg_note (insn, REG_DEAD, st_reg);
> +    }
> +  return true;
> +}
> +
> +/* TARGET_ZERO_CALL_USED_REGS.  */
> +/* Generate a sequence of instructions that zero registers specified by
> +   NEED_ZEROED_HARDREGS.  Return the ZEROED_HARDREGS that are actually
> +   zeroed.  */
> +static HARD_REG_SET
> +ix86_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs)
> +{
> +  HARD_REG_SET zeroed_hardregs;
> +  bool all_sse_zeroed = false;
> +  bool st_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.  */
> +  st_zeroed = zero_all_st_mm_registers (need_zeroed_hardregs);
> +
> +  /* Now, generate instructions to zero all the registers.  */
> +
> +  CLEAR_HARD_REG_SET (zeroed_hardregs);
> +  if (st_zeroed)
> +    SET_HARD_REG_BIT (zeroed_hardregs, FIRST_STACK_REG);
> +
> +  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 +23412,9 @@ ix86_run_selftests (void)
>  #undef TARGET_FUNCTION_VALUE_REGNO_P
>  #define TARGET_FUNCTION_VALUE_REGNO_P ix86_function_value_regno_p
>
> +#undef TARGET_ZERO_CALL_USED_REGS
> +#define TARGET_ZERO_CALL_USED_REGS ix86_zero_call_used_regs
> +
>  #undef TARGET_PROMOTE_FUNCTION_MODE
>  #define TARGET_PROMOTE_FUNCTION_MODE ix86_promote_function_mode
>
> diff --git a/gcc/df-scan.c b/gcc/df-scan.c
> index 93b060f..9e75c13 100644
> --- a/gcc/df-scan.c
> +++ b/gcc/df-scan.c
> @@ -3614,6 +3614,14 @@ df_update_entry_block_defs (void)
>  }
>
>
> +/* Return true if REGNO is used by the epilogue.  */
> +bool
> +df_epilogue_uses_p (unsigned int regno)
> +{
> +  return (EPILOGUE_USES (regno)
> +         || TEST_HARD_REG_BIT (crtl->must_be_zero_on_return, regno));
> +}
> +
>  /* Set the bit for regs that are considered being used at the exit. */
>
>  static void
> @@ -3661,7 +3669,7 @@ df_get_exit_block_use_set (bitmap exit_block_uses)
>       epilogue as being live at the end of the function since they
>       may be referenced by our caller.  */
>    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
> -    if (global_regs[i] || EPILOGUE_USES (i))
> +    if (global_regs[i] || df_epilogue_uses_p (i))
>        bitmap_set_bit (exit_block_uses, i);
>
>    if (targetm.have_epilogue () && epilogue_completed)
> @@ -3802,7 +3810,6 @@ df_hard_reg_init (void)
>    initialized = true;
>  }
>
> -
>  /* Recompute the parts of scanning that are based on regs_ever_live
>     because something changed in that array.  */
>
> @@ -3862,7 +3869,6 @@ df_regs_ever_live_p (unsigned int regno)
>    return regs_ever_live[regno];
>  }
>
> -
>  /* Set regs_ever_live[REGNO] to VALUE.  If this cause regs_ever_live
>     to change, schedule that change for the next update.  */
>
> diff --git a/gcc/df.h b/gcc/df.h
> index 8b6ca8c..0f098d7 100644
> --- a/gcc/df.h
> +++ b/gcc/df.h
> @@ -1085,6 +1085,7 @@ extern void df_update_entry_exit_and_calls (void);
>  extern bool df_hard_reg_used_p (unsigned int);
>  extern unsigned int df_hard_reg_used_count (unsigned int);
>  extern bool df_regs_ever_live_p (unsigned int);
> +extern bool df_epilogue_uses_p (unsigned int);
>  extern void df_set_regs_ever_live (unsigned int, bool);
>  extern void df_compute_regs_ever_live (bool);
>  extern void df_scan_verify (void);
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index c9f7299..3a884e1 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -3992,6 +3992,49 @@ performing a link with relocatable output (i.e.@: 
> @code{ld -r}) on them.
>  A declaration to which @code{weakref} is attached and that is associated
>  with a named @code{target} must be @code{static}.
>
> +@item zero_call_used_regs ("@var{choice}")
> +@cindex @code{zero_call_used_regs} function attribute
> +
> +The @code{zero_call_used_regs} attribute causes the compiler to zero
> +a subset of all call-used registers at function return according to
> +@var{choice}.
> +This is used to increase the program security by either mitigating
> +Return-Oriented Programming (ROP) or preventing information leak
> +through registers.
> +
> +A "call-used" register is a register that is clobbered by function calls,
> +as a result, the caller has to save and restore it before or after a
> +function call.  It is also called as "call-clobbered", "caller-saved", or
> +"volatile".
> +
> +In order to satisfy users with different security needs and control the
> +run-time overhead at the same time,  GCC provides a flexible way to choose
> +the subset of the call-used registers to be zeroed.
> +
> +@samp{skip} doesn't zero any call-used registers.
> +@samp{used} zeros call-used registers which are used in the function.  A 
> "used"
> +register is one whose content has been set or referenced in the function.
> +@samp{all} zeros all call-used registers.
> +
> +In addition to the above three basic choices, the register set can be further
> +limited by adding "-gpr" (i.e., general purpose register), "-arg" (i.e.,
> +argument register), or both as following:
> +
> +@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-gpr-arg} zeros all call-used general purpose registers that pass
> +parameters.
> +@samp{all-arg} zeros all call-used registers that pass parameters.
> +@samp{used-gpr} zeros call-used general purpose registers which are used in 
> the
> +function.
> +@samp{all-gpr} zeros all call-used general purpose registers.
> +
> +Among this list, "used-gpr-arg", "used-arg", "all-gpr-arg", and "all-arg" are
> +mainly used for ROP mitigation.
> +
> +The default for the attribute is controlled by 
> @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..c6837d7 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,46 @@ 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.
> +
> +A "call-used" register is a register that is clobbered by function calls,
> +as a result, the caller has to save and restore it before or after a
> +function call.  It is also called as "call-clobbered", "caller-saved", or
> +"volatile".
> +
> +In order to satisfy users with different security needs and control the
> +run-time overhead at the same time,  GCC provides a flexible way to choose
> +the subset of the call-used registers to be zeroed.
> +
> +@samp{skip}, which is the default, doesn't zero any call-used registers.
> +@samp{used} zeros call-used registers which are used in the function.  A 
> "used"
> +register is one whose content has been set or referenced in the function.
> +@samp{all} zeros all call-used registers.
> +
> +In addition to the above three basic choices, the register set can be further
> +limited by adding "-gpr" (i.e., general purpose register), "-arg" (i.e.,
> +argument register), or both as following:
> +
> +@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-gpr-arg} zeros all call-used general purpose registers that pass
> +parameters.
> +@samp{all-arg} zeros all call-used registers that pass parameters.
> +@samp{used-gpr} zeros call-used general purpose registers which are used in 
> the
> +function.
> +@samp{all-gpr} zeros all call-used general purpose registers.
> +
> +Among this list, "used-gpr-arg", "used-arg", "all-gpr-arg", and "all-arg" are
> +mainly used for ROP mitigation.
> +
> +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..3b75c46 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -12053,6 +12053,18 @@ argument list due to stack realignment.  Return 
> @code{NULL} if no DRAP
>  is needed.
>  @end deftypefn
>
> +@deftypefn {Target Hook} HARD_REG_SET TARGET_ZERO_CALL_USED_REGS 
> (HARD_REG_SET @var{selected_regs})
> +This target hook emits instructions to zero subset of @var{selected_regs}
> +that could conceivably contain values that are useful to an attacker.
> +Return the set of registers that were actually cleared.
> +
> +The default implementation uses normal move instructions to zero
> +all the registers in @var{selected_regs}.  Define this hook if the
> +target has more efficient ways of zeroing certain registers,
> +or if you believe that certain registers would never contain
> +values that are useful to an attacker.
> +@end deftypefn
> +
>  @deftypefn {Target Hook} bool TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS (void)
>  When optimization is disabled, this hook indicates whether or not
>  arguments should be allocated to stack slots.  Normally, GCC allocates
> diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
> index 412e22c..a67dbea 100644
> --- a/gcc/doc/tm.texi.in
> +++ b/gcc/doc/tm.texi.in
> @@ -8111,6 +8111,8 @@ and the associated definitions of those functions.
>
>  @hook TARGET_GET_DRAP_RTX
>
> +@hook TARGET_ZERO_CALL_USED_REGS
> +
>  @hook TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
>
>  @hook TARGET_CONST_ANCHOR
> diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h
> index 92ad0dd6..d7bdb66 100644
> --- a/gcc/emit-rtl.h
> +++ b/gcc/emit-rtl.h
> @@ -173,6 +173,9 @@ struct GTY(()) rtl_data {
>          local stack.  */
>    unsigned int stack_alignment_estimated;
>
> +  /* How to zero call-used regsiters for this routine.  */
> +  unsigned int zero_call_used_regs;
> +
>    /* How many NOP insns to place at each function entry by default.  */
>    unsigned short patch_area_size;
>
> @@ -310,6 +313,9 @@ struct GTY(()) rtl_data {
>       sets them.  */
>    HARD_REG_SET asm_clobbers;
>
> +  /* All hard registers that need to be zeroed at the return of the routine. 
>  */
> +  HARD_REG_SET must_be_zero_on_return;
> +
>    /* The highest address seen during shorten_branches.  */
>    int max_insn_address;
>  };
> diff --git a/gcc/flag-types.h b/gcc/flag-types.h
> index 852ea76..0f7e503 100644
> --- a/gcc/flag-types.h
> +++ b/gcc/flag-types.h
> @@ -285,6 +285,15 @@ enum sanitize_code {
>                                   | SANITIZE_BOUNDS_STRICT
>  };
>
> +enum  zero_call_used_regs_code {
> +  UNSET = 0,
> +  SKIP = 1UL << 0,
> +  ONLY_USED = 1UL << 1,
> +  ONLY_GPR = 1UL << 2,
> +  ONLY_ARG = 1UL << 3,
> +  ALL = 1UL << 4
> +};
> +
>  /* Settings of flag_incremental_link.  */
>  enum incremental_link {
>    INCREMENTAL_LINK_NONE,
> diff --git a/gcc/function.c b/gcc/function.c
> index c612959..56e9997 100644
> --- a/gcc/function.c
> +++ b/gcc/function.c
> @@ -46,10 +46,12 @@ along with GCC; see the file COPYING3.  If not see
>  #include "stringpool.h"
>  #include "expmed.h"
>  #include "optabs.h"
> +#include "opts.h"
>  #include "regs.h"
>  #include "emit-rtl.h"
>  #include "recog.h"
>  #include "rtl-error.h"
> +#include "hard-reg-set.h"
>  #include "alias.h"
>  #include "fold-const.h"
>  #include "stor-layout.h"
> @@ -5815,6 +5817,102 @@ make_prologue_seq (void)
>    return seq;
>  }
>
> +/* 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;
> +
> +  /* 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.  */
> +
> +  gpr_only = crtl->zero_call_used_regs & ONLY_GPR;
> +  used_only = crtl->zero_call_used_regs & ONLY_USED;
> +  arg_only = crtl->zero_call_used_regs & ONLY_ARG;
> +
> +  /* For each of the hard registers, 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;
> +   */
> +
> +  /* First, prepare the data flow information.  */
> +  basic_block bb = BLOCK_FOR_INSN (ret);
> +  bitmap live_out;
> +  live_out = BITMAP_ALLOC (NULL);
> +  bitmap_copy (live_out, df_get_live_out (bb));
> +  df_simulate_initialize_backwards (bb, live_out);
> +  df_simulate_one_insn_backwards (bb, ret, live_out);
> +
> +  HARD_REG_SET need_zeroed_hardregs;
> +  CLEAR_HARD_REG_SET (need_zeroed_hardregs);
> +  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
> +    {
> +      if (!crtl->abi->clobbers_full_reg_p (regno))
> +       continue;
> +      if (fixed_regs[regno])
> +       continue;
> +      if (REGNO_REG_SET_P (live_out, regno))
> +       continue;
> +      if (gpr_only
> +         && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
> +       continue;
> +      if (used_only && !df_regs_ever_live_p (regno))
> +       continue;
> +      if (arg_only && !FUNCTION_ARG_REGNO_P (regno))
> +       continue;
> +
> +      /* Now this is a register that we might want to zero.  */
> +      SET_HARD_REG_BIT (need_zeroed_hardregs, regno);
> +    }
> +
> +  BITMAP_FREE (live_out);
> +
> +  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->must_be_zero_on_return |= zeroed_hardregs;
> +      df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun));
> +    }
> +}
> +
> +
>  /* Return a sequence to be used as the epilogue for the current function,
>     or NULL.  */
>
> @@ -6486,7 +6584,120 @@ 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)
> +{
> +  edge_iterator ei;
> +  edge e;
> +  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_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
> +    {
> +      insn = BB_END (e->src);
> +      if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn)))
> +       gen_call_used_regs_seq (insn);
> +    }
> +
> +  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 *);
> +
> +  virtual unsigned int execute (function *)
> +    {
> +      return rest_of_zero_call_used_regs ();
> +    }
> +
> +}; // class pass_zero_call_used_regs
> +
> +bool
> +pass_zero_call_used_regs::gate (function *fun)
> +{
> +  unsigned int zero_regs_type = UNSET;
> +  unsigned int attr_zero_regs_type = UNSET;
> +
> +  tree attr_zero_regs
> +       = lookup_attribute ("zero_call_used_regs",
> +                           DECL_ATTRIBUTES (fun->decl));
> +
> +  /* Get the type of zero_call_used_regs from function attribute.  */
> +  if (attr_zero_regs)
> +    {
> +      bool found = false;
> +      unsigned int i;
> +
> +      /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
> +        is the attribute argument's value.  */
> +      attr_zero_regs = TREE_VALUE (attr_zero_regs);
> +      gcc_assert (TREE_CODE (attr_zero_regs) == TREE_LIST);
> +      attr_zero_regs = TREE_VALUE (attr_zero_regs);
> +      gcc_assert (TREE_CODE (attr_zero_regs) == STRING_CST);
> +
> +      for (i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
> +       if (strcmp (TREE_STRING_POINTER (attr_zero_regs),
> +                    zero_call_used_regs_opts[i].name) == 0)
> +         {
> +           attr_zero_regs_type |= zero_call_used_regs_opts[i].flag;
> +           found = true;
> +           break;
> +         }
> +
> +      if (!found)
> +       warning_at (DECL_SOURCE_LOCATION (fun->decl), 0,
> +                   "unrecognized zero_call_used_regs attribute: %qs",
> +                   TREE_STRING_POINTER (attr_zero_regs));
> +    }
> +
> +  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;
> +
> +  crtl->zero_call_used_regs = zero_regs_type;
> +
> +  /* No need to zero call-used-regs when no user request is present.  */
> +  return zero_regs_type > SKIP;
> +}
> +
> +} // 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..bd64af0 100644
> --- a/gcc/optabs.c
> +++ b/gcc/optabs.c
> @@ -6484,6 +6484,48 @@ expand_memory_blockage (void)
>      expand_asm_memory_blockage ();
>  }
>
> +/* Generate asm volatile("" : : : "memory") as a memory blockage, at the
> +   same time clobbering the register set specified by REGS.  */
> +
> +void
> +expand_asm_reg_clobber_mem_blockage (HARD_REG_SET regs)
> +{
> +  rtx asm_op, clob_mem;
> +
> +  unsigned int num_of_regs = 0;
> +  for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
> +    if (TEST_HARD_REG_BIT (regs, i))
> +      num_of_regs++;
> +
> +  asm_op = gen_rtx_ASM_OPERANDS (VOIDmode, "", "", 0,
> +                                rtvec_alloc (0), rtvec_alloc (0),
> +                                rtvec_alloc (0), UNKNOWN_LOCATION);
> +  MEM_VOLATILE_P (asm_op) = 1;
> +
> +  rtvec v = rtvec_alloc (num_of_regs + 2);
> +
> +  clob_mem = gen_rtx_SCRATCH (VOIDmode);
> +  clob_mem = gen_rtx_MEM (BLKmode, clob_mem);
> +  clob_mem = gen_rtx_CLOBBER (VOIDmode, clob_mem);
> +
> +  RTVEC_ELT (v,0) = asm_op;
> +  RTVEC_ELT (v,1) = clob_mem;
> +
> +  if (num_of_regs > 0)
> +    {
> +      unsigned int j = 2;
> +      for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
> +       if (TEST_HARD_REG_BIT (regs, i))
> +         {
> +           RTVEC_ELT (v,j) = gen_rtx_CLOBBER (VOIDmode, regno_reg_rtx[i]);
> +           j++;
> +         }
> +      gcc_assert (j == (num_of_regs + 2));
> +    }
> +
> +  emit_insn (gen_rtx_PARALLEL (VOIDmode, v));
> +}
> +
>  /* This routine will either emit the mem_thread_fence pattern or issue a
>     sync_synchronize to generate a fence for memory model MEMMODEL.  */
>
> diff --git a/gcc/optabs.h b/gcc/optabs.h
> index 0b14700..bfa10c8 100644
> --- a/gcc/optabs.h
> +++ b/gcc/optabs.h
> @@ -345,6 +345,8 @@ rtx expand_atomic_store (rtx, rtx, enum memmodel, bool);
>  rtx expand_atomic_fetch_op (rtx, rtx, rtx, enum rtx_code, enum memmodel,
>                               bool);
>
> +extern void expand_asm_reg_clobber_mem_blockage (HARD_REG_SET);
> +
>  extern bool insn_operand_matches (enum insn_code icode, unsigned int opno,
>                                   rtx operand);
>  extern bool valid_multiword_target_p (rtx);
> diff --git a/gcc/opts.c b/gcc/opts.c
> index 3bda59a..f95a1f0 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -1776,6 +1776,24 @@ const struct sanitizer_opts_s 
> coverage_sanitizer_opts[] =
>    { NULL, 0U, 0UL, false }
>  };
>
> +/* -fzero-call-used-regs= suboptions.  */
> +const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
> +{
> +#define ZERO_CALL_USED_REGS_OPT(name, flags) \
> +    { #name, flags }
> +  ZERO_CALL_USED_REGS_OPT (skip, SKIP),
> +  ZERO_CALL_USED_REGS_OPT (used-gpr-arg, (ONLY_USED | ONLY_GPR | ONLY_ARG)),
> +  ZERO_CALL_USED_REGS_OPT (used-arg, (ONLY_USED | ONLY_ARG)),
> +  ZERO_CALL_USED_REGS_OPT (all-gpr-arg, (ONLY_GPR | ONLY_ARG)),
> +  ZERO_CALL_USED_REGS_OPT (all-arg, ONLY_ARG),
> +  ZERO_CALL_USED_REGS_OPT (used-gpr, (ONLY_USED | ONLY_GPR)),
> +  ZERO_CALL_USED_REGS_OPT (all-gpr, ONLY_GPR),
> +  ZERO_CALL_USED_REGS_OPT (used, ONLY_USED),
> +  ZERO_CALL_USED_REGS_OPT (all, ALL),
> +#undef ZERO_CALL_USED_REGS_OPT
> +  {NULL, 0U}
> +};
> +
>  /* A struct for describing a run of chars within a string.  */
>
>  class string_fragment
> @@ -1970,6 +1988,30 @@ parse_no_sanitize_attribute (char *value)
>    return flags;
>  }
>
> +/* Parse -fzero-call-used-regs suboptions from ARG, return the FLAGS.  */
> +
> +unsigned int
> +parse_zero_call_used_regs_options (const char *arg)
> +{
> +  bool found = false;
> +  unsigned int flags = 0;
> +  unsigned int i;
> +
> +  /* Check to see if the string matches a sub-option name.  */
> +  for (i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
> +    if (strcmp (arg, zero_call_used_regs_opts[i].name) == 0)
> +      {
> +       flags |= zero_call_used_regs_opts[i].flag;
> +       found = true;
> +       break;
> +      }
> +
> +  if (!found)
> +    error ("unrecognized argument to %<-fzero-call-used-regs=%>: %qs", arg);
> +
> +  return flags;
> +}
> +
>  /* Parse -falign-NAME format for a FLAG value.  Return individual
>     parsed integer values into RESULT_VALUES array.  If REPORT_ERROR is
>     set, print error message at LOC location.  */
> @@ -2601,6 +2643,11 @@ common_handle_option (struct gcc_options *opts,
>        /* Automatically sets -ftree-loop-vectorize and
>          -ftree-slp-vectorize.  Nothing more to do here.  */
>        break;
> +    case OPT_fzero_call_used_regs_:
> +      opts->x_flag_zero_call_used_regs
> +       = parse_zero_call_used_regs_options (arg);
> +      break;
> +
>      case OPT_fshow_column:
>        dc->show_column = value;
>        break;
> diff --git a/gcc/opts.h b/gcc/opts.h
> index 8f594b4..7d1e126 100644
> --- a/gcc/opts.h
> +++ b/gcc/opts.h
> @@ -444,6 +444,12 @@ extern const struct sanitizer_opts_s
>    bool can_recover;
>  } sanitizer_opts[];
>
> +extern const struct zero_call_used_regs_opts_s
> +{
> +  const char *const name;
> +  unsigned int flag;
> +} zero_call_used_regs_opts[];
> +
>  extern vec<const char *> help_option_arguments;
>
>  extern void add_misspelling_candidates (auto_vec<char *> *candidates,
> diff --git a/gcc/passes.def b/gcc/passes.def
> index f865bdc..77d4676 100644
> --- a/gcc/passes.def
> +++ b/gcc/passes.def
> @@ -492,6 +492,7 @@ along with GCC; see the file COPYING3.  If not see
>        POP_INSERT_PASSES ()
>        NEXT_PASS (pass_late_compilation);
>        PUSH_INSERT_PASSES_WITHIN (pass_late_compilation)
> +         NEXT_PASS (pass_zero_call_used_regs);
>           NEXT_PASS (pass_compute_alignments);
>           NEXT_PASS (pass_variable_tracking);
>           NEXT_PASS (pass_free_cfg);
> diff --git a/gcc/recog.c b/gcc/recog.c
> index ce83b7f..e231b5d 100644
> --- a/gcc/recog.c
> +++ b/gcc/recog.c
> @@ -923,6 +923,22 @@ validate_simplify_insn (rtx_insn *insn)
>    return ((num_changes_pending () > 0) && (apply_change_group () > 0));
>  }
>
>
> +
> +/* Check whether INSN matches a specific alternative of an .md pattern.  */
> +bool
> +valid_insn_p (rtx_insn *insn)
> +{
> +  recog_memoized (insn);
> +  if (INSN_CODE (insn) < 0)
> +    return false;
> +  extract_insn (insn);
> +  /* We don't know whether the insn will be in code that is optimized
> +     for size or speed, so consider all enabled alternatives.  */
> +  if (!constrain_operands (1, get_enabled_alternatives (insn)))
> +    return false;
> +  return true;
> +}
> +
>  /* Return 1 if OP is a valid general operand for machine mode MODE.
>     This is either a register reference, a memory reference,
>     or a constant.  In the case of a memory reference, the address
> diff --git a/gcc/recog.h b/gcc/recog.h
> index ae3675f..d87456c 100644
> --- a/gcc/recog.h
> +++ b/gcc/recog.h
> @@ -113,6 +113,7 @@ extern void validate_replace_src_group (rtx, rtx, 
> rtx_insn *);
>  extern bool validate_simplify_insn (rtx_insn *insn);
>  extern int num_changes_pending (void);
>  extern bool reg_fits_class_p (const_rtx, reg_class_t, int, machine_mode);
> +extern bool valid_insn_p (rtx_insn *);
>
>  extern int offsettable_memref_p (rtx);
>  extern int offsettable_nonstrict_memref_p (rtx);
> diff --git a/gcc/resource.c b/gcc/resource.c
> index 0a9d594..90cf091 100644
> --- a/gcc/resource.c
> +++ b/gcc/resource.c
> @@ -1186,7 +1186,7 @@ init_resource_info (rtx_insn *epilogue_insn)
>                                &end_of_function_needs, true);
>
>    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
> -    if (global_regs[i] || EPILOGUE_USES (i))
> +    if (global_regs[i] || df_epilogue_uses_p (i))
>        SET_HARD_REG_BIT (end_of_function_needs.regs, i);
>
>    /* The registers required to be live at the end of the function are
> diff --git a/gcc/target.def b/gcc/target.def
> index ed2da15..20e7f81 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -5080,6 +5080,21 @@ argument list due to stack realignment.  Return 
> @code{NULL} if no DRAP\n\
>  is needed.",
>   rtx, (void), NULL)
>
> +/* Generate instruction sequence to zero call used registers.  */
> +DEFHOOK
> +(zero_call_used_regs,
> + "This target hook emits instructions to zero subset of 
> @var{selected_regs}\n\
> +that could conceivably contain values that are useful to an attacker.\n\
> +Return the set of registers that were actually cleared.\n\
> +\n\
> +The default implementation uses normal move instructions to zero\n\
> +all the registers in @var{selected_regs}.  Define this hook if the\n\
> +target has more efficient ways of zeroing certain registers,\n\
> +or if you believe that certain registers would never contain\n\
> +values that are useful to an attacker.",
> + HARD_REG_SET, (HARD_REG_SET selected_regs),
> +default_zero_call_used_regs)
> +
>  /* Return true if all function parameters should be spilled to the
>     stack.  */
>  DEFHOOK
> diff --git a/gcc/targhooks.c b/gcc/targhooks.c
> index 5d94fce..88eef00 100644
> --- a/gcc/targhooks.c
> +++ b/gcc/targhooks.c
> @@ -56,6 +56,9 @@ along with GCC; see the file COPYING3.  If not see
>  #include "tree-ssa-alias.h"
>  #include "gimple-expr.h"
>  #include "memmodel.h"
> +#include "backend.h"
> +#include "emit-rtl.h"
> +#include "df.h"
>  #include "tm_p.h"
>  #include "stringpool.h"
>  #include "tree-vrp.h"
> @@ -987,6 +990,35 @@ default_function_value_regno_p (const unsigned int regno 
> ATTRIBUTE_UNUSED)
>  #endif
>  }
>
> +/* The default hook for TARGET_ZERO_CALL_USED_REGS.  */
> +
> +HARD_REG_SET
> +default_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs)
> +{
> +  gcc_assert (!hard_reg_set_empty_p (need_zeroed_hardregs));
> +
> +  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
> +    if (TEST_HARD_REG_BIT (need_zeroed_hardregs, regno))
> +      {
> +       rtx_insn *last_insn = get_last_insn ();
> +       machine_mode mode = GET_MODE (regno_reg_rtx[regno]);
> +       rtx zero = CONST0_RTX (mode);
> +       rtx_insn *insn = emit_move_insn (regno_reg_rtx[regno], zero);
> +       if (!valid_insn_p (insn))
> +         {
> +           static bool issued_error;
> +           if (!issued_error)
> +             {
> +               issued_error = true;
> +               sorry ("%qs not supported on this target",
> +                       "fzero-call-used_regs");
> +             }
> +           delete_insns_since (last_insn);
> +         }
> +      }
> +  return need_zeroed_hardregs;
> +}
> +
>  rtx
>  default_internal_arg_pointer (void)
>  {
> diff --git a/gcc/targhooks.h b/gcc/targhooks.h
> index 44ab926..e0a925f 100644
> --- a/gcc/targhooks.h
> +++ b/gcc/targhooks.h
> @@ -160,6 +160,7 @@ extern unsigned int default_function_arg_round_boundary 
> (machine_mode,
>                                                          const_tree);
>  extern bool hook_bool_const_rtx_commutative_p (const_rtx, int);
>  extern rtx default_function_value (const_tree, const_tree, bool);
> +extern HARD_REG_SET default_zero_call_used_regs (HARD_REG_SET);
>  extern rtx default_libcall_value (machine_mode, const_rtx);
>  extern bool default_function_value_regno_p (const unsigned int);
>  extern rtx default_internal_arg_pointer (void);
> diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c 
> b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c
> new file mode 100644
> index 0000000..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..0258c70
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c
> @@ -0,0 +1,21 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */
> +
> +void
> +foo (void)
> +{
> +}
> +
> +/* { dg-final { scan-assembler "vzeroall" } } */
> +/* { dg-final { scan-assembler-times "fldz" 8 } } */
> +/* { dg-final { scan-assembler-times "fstp" 8 } } */
> +/* { dg-final { scan-assembler-not "%xmm" } } */
> +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } 
> } } } */
> diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c 
> b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c
> new file mode 100644
> index 0000000..0625eb5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c
> @@ -0,0 +1,29 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx512f" } */
> +
> +void
> +foo (void)
> +{
> +}
> +
> +/* { dg-final { scan-assembler "vzeroall" } } */
> +/* { dg-final { scan-assembler-times "fldz" 8 } } */
> +/* { dg-final { scan-assembler-times "fstp" 8 } } */
> +/* { dg-final { scan-assembler-not "%xmm" } } */
> +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } 
> } } } */
> +/* { dg-final { scan-assembler "kxorw\[ \t\]*%k0, %k0, %k0" { target { ! 
> ia32 } } } } */
> +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k1" { target { ! ia32 } } 
> } } */
> +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k2" { target { ! ia32 } } 
> } } */
> +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k3" { target { ! ia32 } } 
> } } */
> +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k4" { target { ! ia32 } } 
> } } */
> +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k5" { target { ! ia32 } } 
> } } */
> +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k6" { target { ! ia32 } } 
> } } */
> +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k7" { target { ! ia32 } } 
> } } */
> diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c 
> b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c
> new file mode 100644
> index 0000000..208633e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */
> +
> +int
> +foo (int x)
> +{
> +  return x;
> +}
> +
> +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" } } */
> diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c 
> b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c
> new file mode 100644
> index 0000000..21e82c6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */
> +
> +int
> +foo (int x)
> +{
> +  return x;
> +}
> +
> +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" } } */
> diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c 
> b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c
> new file mode 100644
> index 0000000..293d2fe
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c
> @@ -0,0 +1,23 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */
> +
> +int
> +foo (int x)
> +{
> +  return x;
> +}
> +
> +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" } } */
> +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */
> +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm1" } } */
> +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm2" } } */
> +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm3" } } */
> +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm4" } } */
> +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm5" } } */
> +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm6" } } */
> +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm7" } } */
> diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c 
> b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c
> new file mode 100644
> index 0000000..c34e6af
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c
> @@ -0,0 +1,15 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */
> +
> +int
> +foo (int x)
> +{
> +  return x;
> +}
> +
> +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" } } */
> +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" } } */
> diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-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