Richard and Uros,
Could you please review the change that H.J and I rewrote based on your comments in the previous round of discussion? This patch is a nice security enhancement for GCC that has been requested by security people for quite some time. Thanks a lot for your time. Qing > On Jul 14, 2020, at 9:45 AM, Qing Zhao via Gcc-patches > <gcc-patches@gcc.gnu.org> wrote: > > Hi, Gcc team, > > This patch is a follow-up on the previous patch and corresponding discussion: > https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545101.html > <https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545101.html> > > From the previous round of discussion, the major issues raised were: > > A. should be rewritten by using regsets infrastructure. > B. Put the patch into middle-end instead of x86 backend. > > This new patch is rewritten based on the above 2 comments. The major changes > compared to the previous patch are: > > 1. Change the names of the option and attribute from > -mzero-caller-saved-regs=[skip|used-gpr|all-gpr|used|all] and > zero_caller_saved_regs("skip|used-gpr|all-gpr||used|all”) > to: > -fzero-call-used-regs=[skip|used-gpr|all-gpr|used|all] and > zero_call_used_regs("skip|used-gpr|all-gpr||used|all”) > Add the new option and new attribute in general. > 2. The main code generation part is moved from i386 backend to middle-end; > 3. Add 4 target-hooks; > 4. Implement these 4 target-hooks on i386 backend. > 5. On a target that does not implement the target hook, issue error for the > new option, issue warning for the new attribute. > > The patch is as following: > > [PATCH] Add -fzero-call-used-regs=[skip|used-gpr|all-gpr|used|all] > command-line option and > zero_call_used_regs("skip|used-gpr|all-gpr||used|all") function attribue: > > 1. -fzero-call-used-regs=skip and zero_call_used_regs("skip") > > Don't zero call-used registers upon function return. > > 2. -fzero-call-used-regs=used-gpr and zero_call_used_regs("used-gpr") > > Zero used call-used general purpose registers upon function return. > > 3. -fzero-call-used-regs=all-gpr and zero_call_used_regs("all-gpr") > > Zero all call-used general purpose registers upon function return. > > 4. -fzero-call-used-regs=used and zero_call_used_regs("used") > > Zero used call-used registers upon function return. > > 5. -fzero-call-used-regs=all and zero_call_used_regs("all") > > Zero all call-used registers upon function return. > > The feature is implemented in middle-end. But currently is only valid on X86. > > Tested on x86-64 and aarch64 with bootstrapping GCC trunk, making > -fzero-call-used-regs=used-gpr, -fzero-call-used-regs=all-gpr > -fzero-call-used-regs=used, and -fzero-call-used-regs=all enabled > by default on x86-64. > > Please take a look and let me know any more comment? > > thanks. > > Qing > > > ==================================== > > gcc/ChangeLog: > > 2020-07-13 qing zhao <qing.z...@oracle.com <mailto:qing.z...@oracle.com>> > 2020-07-13 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 (ix86_zero_call_used_regno_p): New function. > (ix86_zero_call_used_regno_mode): Likewise. > (ix86_zero_all_vector_registers): Likewise. > (ix86_expand_prologue): Replace gen_prologue_use with > gen_pro_epilogue_use. > (TARGET_ZERO_CALL_USED_REGNO_P): Define. > (TARGET_ZERO_CALL_USED_REGNO_MODE): Define. > (TARGET_PRO_EPILOGUE_USE): Define. > (TARGET_ZERO_ALL_VECTOR_REGISTERS): Define. > * config/i386/i386.md: Replace UNSPECV_PROLOGUE_USE > with UNSPECV_PRO_EPILOGUE_USE. > * coretypes.h (enum zero_call_used_regs): New type. > * doc/extend.texi: Document the new zero_call_used_regs attribute. > * doc/invoke.texi: Document the new -fzero-call-used-regs option. > * doc/tm.texi: Regenerate. > * doc/tm.texi.in (TARGET_ZERO_CALL_USED_REGNO_P): New hook. > (TARGET_ZERO_CALL_USED_REGNO_MODE): Likewise. > (TARGET_PRO_EPILOGUE_USE): Likewise. > (TARGET_ZERO_ALL_VECTOR_REGISTERS): Likewise. > * function.c (is_live_reg_at_exit): New function. > (gen_call_used_regs_seq): Likewise. > (make_epilogue_seq): Call gen_call_used_regs_seq. > * function.h (is_live_reg_at_exit): Declare. > * target.def (zero_call_used_regno_p): New hook. > (zero_call_used_regno_mode): Likewise. > (pro_epilogue_use): Likewise. > (zero_all_vector_registers): Likewise. > * targhooks.c (default_zero_call_used_regno_p): New function. > (default_zero_call_used_regno_mode): Likewise. > * targhooks.h (default_zero_call_used_regno_p): Declare. > (default_zero_call_used_regno_mode): Declare. > * toplev.c (process_options): Issue errors when -fzero-call-used-regs > is used on targets that do not support it. > * tree-core.h (struct tree_decl_with_vis): New field > zero_call_used_regs_type. > * tree.h (DECL_ZERO_CALL_USED_REGS): New macro. > > gcc/c-family/ChangeLog: > > 2020-07-13 qing zhao <qing.z...@oracle.com <mailto:qing.z...@oracle.com>> > 2020-07-13 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/c/ChangeLog: > > 2020-07-13 qing zhao <qing.z...@oracle.com <mailto:qing.z...@oracle.com>> > 2020-07-13 H.J. Lu <hjl.to...@gmail.com <mailto:hjl.to...@gmail.com>> > > * c-decl.c (merge_decls): Merge zero_call_used_regs_type. > > gcc/testsuite/ChangeLog: > > 2020-07-13 qing zhao <qing.z...@oracle.com <mailto:qing.z...@oracle.com>> > 2020-07-13 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: Likewise. > * gcc.target/i386/zero-scratch-regs-1.c: Likewise. > * gcc.target/i386/zero-scratch-regs-10.c: Likewise. > * gcc.target/i386/zero-scratch-regs-11.c: Likewise. > * gcc.target/i386/zero-scratch-regs-12.c: Likewise. > * gcc.target/i386/zero-scratch-regs-13.c: Likewise. > * gcc.target/i386/zero-scratch-regs-14.c: Likewise. > * gcc.target/i386/zero-scratch-regs-15.c: Likewise. > * gcc.target/i386/zero-scratch-regs-16.c: Likewise. > * gcc.target/i386/zero-scratch-regs-17.c: Likewise. > * gcc.target/i386/zero-scratch-regs-18.c: Likewise. > * gcc.target/i386/zero-scratch-regs-19.c: Likewise. > * gcc.target/i386/zero-scratch-regs-2.c: Likewise. > * gcc.target/i386/zero-scratch-regs-20.c: Likewise. > * gcc.target/i386/zero-scratch-regs-21.c: Likewise. > * gcc.target/i386/zero-scratch-regs-22.c: Likewise. > * gcc.target/i386/zero-scratch-regs-23.c: Likewise. > * gcc.target/i386/zero-scratch-regs-3.c: Likewise. > * gcc.target/i386/zero-scratch-regs-4.c: Likewise. > * gcc.target/i386/zero-scratch-regs-5.c: Likewise. > * gcc.target/i386/zero-scratch-regs-6.c: Likewise. > * gcc.target/i386/zero-scratch-regs-7.c: Likewise. > * gcc.target/i386/zero-scratch-regs-8.c: Likewise. > * gcc.target/i386/zero-scratch-regs-9.c: Likewise. > > --- > gcc/c-family/c-attribs.c | 68 ++++++++++ > gcc/c/c-decl.c | 4 + > gcc/common.opt | 23 ++++ > gcc/config/i386/i386.c | 58 ++++++++- > gcc/config/i386/i386.md | 6 +- > gcc/coretypes.h | 10 ++ > gcc/doc/extend.texi | 11 ++ > gcc/doc/invoke.texi | 13 +- > gcc/doc/tm.texi | 27 ++++ > gcc/doc/tm.texi.in | 8 ++ > gcc/function.c | 145 +++++++++++++++++++++ > gcc/function.h | 2 + > gcc/target.def | 33 +++++ > gcc/targhooks.c | 17 +++ > gcc/targhooks.h | 3 + > gcc/testsuite/c-c++-common/zero-scratch-regs-1.c | 3 + > gcc/testsuite/c-c++-common/zero-scratch-regs-2.c | 4 + > .../gcc.target/i386/zero-scratch-regs-1.c | 12 ++ > .../gcc.target/i386/zero-scratch-regs-10.c | 21 +++ > .../gcc.target/i386/zero-scratch-regs-11.c | 39 ++++++ > .../gcc.target/i386/zero-scratch-regs-12.c | 39 ++++++ > .../gcc.target/i386/zero-scratch-regs-13.c | 21 +++ > .../gcc.target/i386/zero-scratch-regs-14.c | 19 +++ > .../gcc.target/i386/zero-scratch-regs-15.c | 14 ++ > .../gcc.target/i386/zero-scratch-regs-16.c | 14 ++ > .../gcc.target/i386/zero-scratch-regs-17.c | 13 ++ > .../gcc.target/i386/zero-scratch-regs-18.c | 13 ++ > .../gcc.target/i386/zero-scratch-regs-19.c | 12 ++ > .../gcc.target/i386/zero-scratch-regs-2.c | 19 +++ > .../gcc.target/i386/zero-scratch-regs-20.c | 23 ++++ > .../gcc.target/i386/zero-scratch-regs-21.c | 14 ++ > .../gcc.target/i386/zero-scratch-regs-22.c | 19 +++ > .../gcc.target/i386/zero-scratch-regs-23.c | 19 +++ > .../gcc.target/i386/zero-scratch-regs-3.c | 12 ++ > .../gcc.target/i386/zero-scratch-regs-4.c | 14 ++ > .../gcc.target/i386/zero-scratch-regs-5.c | 20 +++ > .../gcc.target/i386/zero-scratch-regs-6.c | 14 ++ > .../gcc.target/i386/zero-scratch-regs-7.c | 13 ++ > .../gcc.target/i386/zero-scratch-regs-8.c | 19 +++ > .../gcc.target/i386/zero-scratch-regs-9.c | 15 +++ > gcc/toplev.c | 9 ++ > gcc/tree-core.h | 6 +- > gcc/tree.h | 5 + > 43 files changed, 866 insertions(+), 7 deletions(-) > create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-1.c > create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-2.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c > create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c > > diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c > index 3721483..cc93d6f 100644 > --- a/gcc/c-family/c-attribs.c > +++ b/gcc/c-family/c-attribs.c > @@ -136,6 +136,8 @@ static tree handle_target_clones_attribute (tree *, tree, > tree, int, bool *); > static tree handle_optimize_attribute (tree *, tree, tree, int, bool *); > static tree ignore_attribute (tree *, tree, tree, int, bool *); > static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *); > +static tree handle_zero_call_used_regs_attribute (tree *, tree, tree, int, > + bool *); > static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *); > static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *); > static tree handle_returns_nonnull_attribute (tree *, tree, tree, int, bool > *); > @@ -434,6 +436,9 @@ const struct attribute_spec c_common_attribute_table[] = > ignore_attribute, NULL }, > { "no_split_stack", 0, 0, true, false, false, false, > handle_no_split_stack_attribute, NULL }, > + { "zero_call_used_regs", 1, 1, true, false, false, false, > + handle_zero_call_used_regs_attribute, NULL }, > + > /* For internal use (marking of builtins and runtime functions) only. > The name contains space to prevent its usage in source code. */ > { "fn spec", 1, 1, false, true, true, false, > @@ -4506,6 +4511,69 @@ handle_no_split_stack_attribute (tree *node, tree name, > return NULL_TREE; > } > > +/* Handle a "zero_call_used_regs" attribute; arguments as in > + struct attribute_spec.handler. */ > + > +static tree > +handle_zero_call_used_regs_attribute (tree *node, tree name, tree args, > + int ARG_UNUSED (flags), > + bool *no_add_attris) > +{ > + tree decl = *node; > + tree id = TREE_VALUE (args); > + enum zero_call_used_regs zero_call_used_regs_type = > zero_call_used_regs_unset; > + > + if (TREE_CODE (decl) != FUNCTION_DECL) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute applies only to functions", name); > + *no_add_attris = true; > + return NULL_TREE; > + } > + else if (DECL_INITIAL (decl)) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "cannot set %qE attribute after definition", name); > + *no_add_attris = true; > + return NULL_TREE; > + } > + > + if (TREE_CODE (id) != STRING_CST) > + { > + error ("attribute %qE arguments not a string", name); > + *no_add_attris = true; > + return NULL_TREE; > + } > + > + if (!targetm.calls.pro_epilogue_use) > + { > + warning (OPT_Wattributes, "%qE attribute directive ignored", name); > + return NULL_TREE; > + } > + > + if (strcmp (TREE_STRING_POINTER (id), "skip") == 0) > + zero_call_used_regs_type = zero_call_used_regs_skip; > + else if (strcmp (TREE_STRING_POINTER (id), "used-gpr") == 0) > + zero_call_used_regs_type = zero_call_used_regs_used_gpr; > + else if (strcmp (TREE_STRING_POINTER (id), "all-gpr") == 0) > + zero_call_used_regs_type = zero_call_used_regs_all_gpr; > + else if (strcmp (TREE_STRING_POINTER (id), "used") == 0) > + zero_call_used_regs_type = zero_call_used_regs_used; > + else if (strcmp (TREE_STRING_POINTER (id), "all") == 0) > + zero_call_used_regs_type = zero_call_used_regs_all; > + else > + { > + error ("attribute %qE argument must be one of %qs, %qs, %qs, %qs, or > %qs", > + name, "skip", "used-gpr", "all-gpr", "used", "all"); > + *no_add_attris = true; > + return NULL_TREE; > + } > + > + DECL_ZERO_CALL_USED_REGS (decl) = zero_call_used_regs_type; > + > + return NULL_TREE; > +} > + > /* Handle a "returns_nonnull" attribute; arguments as in > struct attribute_spec.handler. */ > > diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c > index 81bd2ee..ded1880 100644 > --- a/gcc/c/c-decl.c > +++ b/gcc/c/c-decl.c > @@ -2681,6 +2681,10 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, > tree oldtype) > DECL_IS_NOVOPS (newdecl) |= DECL_IS_NOVOPS (olddecl); > } > > + /* Merge the zero_call_used_regs_type information. */ > + if (TREE_CODE (newdecl) == FUNCTION_DECL) > + DECL_ZERO_CALL_USED_REGS (newdecl) = DECL_ZERO_CALL_USED_REGS (olddecl); > + > /* Merge the storage class information. */ > merge_weak (newdecl, olddecl); > > diff --git a/gcc/common.opt b/gcc/common.opt > index df8af36..19900f9 100644 > --- a/gcc/common.opt > +++ b/gcc/common.opt > @@ -3083,6 +3083,29 @@ fzero-initialized-in-bss > Common Report Var(flag_zero_initialized_in_bss) Init(1) > Put zero initialized data in the bss section. > > +fzero-call-used-regs= > +Common Report RejectNegative Joined Enum(zero_call_used_regs) > Var(flag_zero_call_used_regs) Init(zero_call_used_regs_skip) > +Clear call-used registers upon function return. > + > +Enum > +Name(zero_call_used_regs) Type(enum zero_call_used_regs) > +Known choices of clearing call-used registers upon function return (for use > with the -fzero-call-used-regs= option): > + > +EnumValue > +Enum(zero_call_used_regs) String(skip) Value(zero_call_used_regs_skip) > + > +EnumValue > +Enum(zero_call_used_regs) String(used-gpr) > Value(zero_call_used_regs_used_gpr) > + > +EnumValue > +Enum(zero_call_used_regs) String(all-gpr) Value(zero_call_used_regs_all_gpr) > + > +EnumValue > +Enum(zero_call_used_regs) String(used) Value(zero_call_used_regs_used) > + > +EnumValue > +Enum(zero_call_used_regs) String(all) Value(zero_call_used_regs_all) > + > g > Common Driver RejectNegative JoinedOrMissing > Generate debug information in default format. > diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c > index 5c373c0..fd1aa9c 100644 > --- a/gcc/config/i386/i386.c > +++ b/gcc/config/i386/i386.c > @@ -3551,6 +3551,48 @@ ix86_function_value_regno_p (const unsigned int regno) > return false; > } > > +/* TARGET_ZERO_CALL_USED_REGNO_P. */ > + > +static bool > +ix86_zero_call_used_regno_p (const unsigned int regno, > + bool gpr_only) > +{ > + return GENERAL_REGNO_P (regno) || (!gpr_only && SSE_REGNO_P (regno)); > +} > + > +/* TARGET_ZERO_CALL_USED_REGNO_MODE. */ > + > +static machine_mode > +ix86_zero_call_used_regno_mode (const unsigned int regno, machine_mode) > +{ > + /* NB: We only need to zero the lower 32 bits for integer registers > + and the lower 128 bits for vector registers since destination are > + zero-extended to the full register width. */ > + return GENERAL_REGNO_P (regno) ? SImode : V4SFmode; > +} > + > +/* TARGET_ZERO_ALL_VECTOR_REGISTERS. */ > + > +static rtx > +ix86_zero_all_vector_registers (bool used_only) > +{ > + if (!TARGET_AVX) > + return NULL; > + > + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) > + if ((IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG) > + || (TARGET_64BIT > + && (REX_SSE_REGNO_P (regno) > + || (TARGET_AVX512F && EXT_REX_SSE_REGNO_P (regno))))) > + && (!this_target_hard_regs->x_call_used_regs[regno] > + || fixed_regs[regno] > + || is_live_reg_at_exit (regno) > + || (used_only && !df_regs_ever_live_p (regno)))) > + return NULL; > + > + return gen_avx_vzeroall (); > +} > + > /* Define how to find the value returned by a function. > VALTYPE is the data type of the value (as a tree). > If the precise function being called is known, FUNC is its FUNCTION_DECL; > @@ -8513,7 +8555,7 @@ ix86_expand_prologue (void) > insn = emit_insn (gen_set_got (pic)); > RTX_FRAME_RELATED_P (insn) = 1; > add_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL_RTX); > - emit_insn (gen_prologue_use (pic)); > + emit_insn (gen_pro_epilogue_use (pic)); > /* Deleting already emmitted SET_GOT if exist and allocated to > REAL_PIC_OFFSET_TABLE_REGNUM. */ > ix86_elim_entry_set_got (pic); > @@ -8542,7 +8584,7 @@ ix86_expand_prologue (void) > Further, prevent alloca modifications to the stack pointer from being > combined with prologue modifications. */ > if (TARGET_SEH) > - emit_insn (gen_prologue_use (stack_pointer_rtx)); > + emit_insn (gen_pro_epilogue_use (stack_pointer_rtx)); > } > > /* Emit code to restore REG using a POP insn. */ > @@ -23319,6 +23361,18 @@ ix86_run_selftests (void) > #undef TARGET_FUNCTION_VALUE_REGNO_P > #define TARGET_FUNCTION_VALUE_REGNO_P ix86_function_value_regno_p > > +#undef TARGET_ZERO_CALL_USED_REGNO_P > +#define TARGET_ZERO_CALL_USED_REGNO_P ix86_zero_call_used_regno_p > + > +#undef TARGET_ZERO_CALL_USED_REGNO_MODE > +#define TARGET_ZERO_CALL_USED_REGNO_MODE ix86_zero_call_used_regno_mode > + > +#undef TARGET_PRO_EPILOGUE_USE > +#define TARGET_PRO_EPILOGUE_USE gen_pro_epilogue_use > + > +#undef TARGET_ZERO_ALL_VECTOR_REGISTERS > +#define TARGET_ZERO_ALL_VECTOR_REGISTERS ix86_zero_all_vector_registers > + > #undef TARGET_PROMOTE_FUNCTION_MODE > #define TARGET_PROMOTE_FUNCTION_MODE ix86_promote_function_mode > > diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md > index d0ecd9e..e7df59f 100644 > --- a/gcc/config/i386/i386.md > +++ b/gcc/config/i386/i386.md > @@ -194,7 +194,7 @@ > UNSPECV_STACK_PROBE > UNSPECV_PROBE_STACK_RANGE > UNSPECV_ALIGN > - UNSPECV_PROLOGUE_USE > + UNSPECV_PRO_EPILOGUE_USE > UNSPECV_SPLIT_STACK_RETURN > UNSPECV_CLD > UNSPECV_NOPS > @@ -13525,8 +13525,8 @@ > > ;; As USE insns aren't meaningful after reload, this is used instead > ;; to prevent deleting instructions setting registers for PIC code > -(define_insn "prologue_use" > - [(unspec_volatile [(match_operand 0)] UNSPECV_PROLOGUE_USE)] > +(define_insn "pro_epilogue_use" > + [(unspec_volatile [(match_operand 0)] UNSPECV_PRO_EPILOGUE_USE)] > "" > "" > [(set_attr "length" "0")]) > diff --git a/gcc/coretypes.h b/gcc/coretypes.h > index 6b6cfcd..e56d6ec 100644 > --- a/gcc/coretypes.h > +++ b/gcc/coretypes.h > @@ -418,6 +418,16 @@ enum symbol_visibility > VISIBILITY_INTERNAL > }; > > +/* Zero call-used registers type. */ > +enum zero_call_used_regs { > + zero_call_used_regs_unset = 0, > + zero_call_used_regs_skip, > + zero_call_used_regs_used_gpr, > + zero_call_used_regs_all_gpr, > + zero_call_used_regs_used, > + zero_call_used_regs_all > +}; > + > /* enums used by the targetm.excess_precision hook. */ > > enum flt_eval_method > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index c800b74..b32c55f 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -3984,6 +3984,17 @@ performing a link with relocatable output (i.e.@: > @code{ld -r}) on them. > A declaration to which @code{weakref} is attached and that is associated > with a named @code{target} must be @code{static}. > > +@item zero_call_used_regs ("@var{choice}") > +@cindex @code{zero_call_used_regs} function attribute > +The @code{zero_call_used_regs} attribute causes the compiler to zero > +call-used registers at function return according to @var{choice}. > +@samp{skip} doesn't zero call-used registers. @samp{used-gpr} zeros > +call-used general purpose registers which are used in funciton. > +@samp{all-gpr} zeros all call-used general purpose registers. > +@samp{used} zeros call-used registers which are used in function. > +@samp{all} zeros all call-used registers. The default for the > +attribute is controlled by @option{-fzero-call-used-regs}. > + > @end table > > @c This is the end of the target-independent attribute table > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > index 09bcc5b..da02686 100644 > --- a/gcc/doc/invoke.texi > +++ b/gcc/doc/invoke.texi > @@ -542,7 +542,7 @@ Objective-C and Objective-C++ Dialects}. > -funit-at-a-time -funroll-all-loops -funroll-loops @gol > -funsafe-math-optimizations -funswitch-loops @gol > -fipa-ra -fvariable-expansion-in-unroller -fvect-cost-model -fvpt @gol > --fweb -fwhole-program -fwpa -fuse-linker-plugin @gol > +-fweb -fwhole-program -fwpa -fuse-linker-plugin -fzero-call-used-regs @gol > --param @var{name}=@var{value} > -O -O0 -O1 -O2 -O3 -Os -Ofast -Og} > > @@ -12273,6 +12273,17 @@ int foo (void) > > Not all targets support this option. > > +@item -fzero-call-used-regs=@var{choice} > +@opindex fzero-call-used-regs > +Zero call-used registers at function return according to > +@var{choice}. @samp{skip}, which is the default, doesn't zero > +call-used registers. @samp{used-gpr} zeros call-used general purpose > +registers which are used in function. @samp{all-gpr} zeros all > +call-used registers. @samp{used} zeros call-used registers which > +are used in function. @samp{all} zeros all call-used registers. You > +can control this behavior for a specific function by using the function > +attribute @code{zero_call_used_regs}. @xref{Function Attributes}. > + > @item --param @var{name}=@var{value} > @opindex param > In some places, GCC uses various constants to control the amount of > diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi > index 6e7d9dc..43dddd3 100644 > --- a/gcc/doc/tm.texi > +++ b/gcc/doc/tm.texi > @@ -4571,6 +4571,22 @@ should recognize only the caller's register numbers. > If this hook is not defined, then FUNCTION_VALUE_REGNO_P will be used. > @end deftypefn > > +@deftypefn {Target Hook} bool TARGET_ZERO_CALL_USED_REGNO_P (const unsigned > int @var{regno}, bool @var{general_reg_only_p}) > +A target hook that returns @code{true} if @var{regno} is the number of a > +call used register. If @var{general_reg_only_p} is @code{true}, > +@var{regno} must be the number of a hard general register. > + > +If this hook is not defined, then default_zero_call_used_regno_p will be > used. > +@end deftypefn > + > +@deftypefn {Target Hook} machine_mode TARGET_ZERO_CALL_USED_REGNO_MODE > (const unsigned int @var{regno}, machine_mode @var{mode}) > +A target hook that returns a mode of suitable to zero the register for the > +call used register @var{regno} in @var{mode}. > + > +If this hook is not defined, then default_zero_call_used_regno_mode will be > +used. > +@end deftypefn > + > @defmac APPLY_RESULT_SIZE > Define this macro if @samp{untyped_call} and @samp{untyped_return} > need more space than is implied by @code{FUNCTION_VALUE_REGNO_P} for > @@ -12043,6 +12059,17 @@ argument list due to stack realignment. Return > @code{NULL} if no DRAP > is needed. > @end deftypefn > > +@deftypefn {Target Hook} rtx TARGET_PRO_EPILOGUE_USE (rtx @var{reg}) > +This hook should return a UNSPEC_VOLATILE rtx to mark a register in use to > +prevent deleting register setting instructions in proprologue and epilogue. > +@end deftypefn > + > +@deftypefn {Target Hook} rtx TARGET_ZERO_ALL_VECTOR_REGISTERS (bool > @var{used_only}) > +This hook should return an rtx to zero all vector registers at function > +exit. If @var{used_only} is @code{true}, only used vector registers should > +be zeroed. Return @code{NULL} if possible > +@end deftypefn > + > @deftypefn {Target Hook} bool TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS (void) > When optimization is disabled, this hook indicates whether or not > arguments should be allocated to stack slots. Normally, GCC allocates > diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in > index 3be984b..bee917a 100644 > --- a/gcc/doc/tm.texi.in > +++ b/gcc/doc/tm.texi.in > @@ -3430,6 +3430,10 @@ for a new target instead. > > @hook TARGET_FUNCTION_VALUE_REGNO_P > > +@hook TARGET_ZERO_CALL_USED_REGNO_P > + > +@hook TARGET_ZERO_CALL_USED_REGNO_MODE > + > @defmac APPLY_RESULT_SIZE > Define this macro if @samp{untyped_call} and @samp{untyped_return} > need more space than is implied by @code{FUNCTION_VALUE_REGNO_P} for > @@ -8109,6 +8113,10 @@ and the associated definitions of those functions. > > @hook TARGET_GET_DRAP_RTX > > +@hook TARGET_PRO_EPILOGUE_USE > + > +@hook TARGET_ZERO_ALL_VECTOR_REGISTERS > + > @hook TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS > > @hook TARGET_CONST_ANCHOR > diff --git a/gcc/function.c b/gcc/function.c > index 9eee9b5..9908530 100644 > --- a/gcc/function.c > +++ b/gcc/function.c > @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see > #include "emit-rtl.h" > #include "recog.h" > #include "rtl-error.h" > +#include "hard-reg-set.h" > #include "alias.h" > #include "fold-const.h" > #include "stor-layout.h" > @@ -5808,6 +5809,147 @@ make_prologue_seq (void) > return seq; > } > > +/* Check whether the hard register REGNO is live at the exit block > + * of the current routine. */ > +bool > +is_live_reg_at_exit (unsigned int regno) > +{ > + edge e; > + edge_iterator ei; > + > + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) > + { > + bitmap live_out = df_get_live_out (e->src); > + if (REGNO_REG_SET_P (live_out, regno)) > + return true; > + } > + > + return false; > +} > + > +/* Emit a sequence of insns to zero the call-used-registers for the current > + * function. */ > + > +static void > +gen_call_used_regs_seq (void) > +{ > + if (!targetm.calls.pro_epilogue_use) > + return; > + > + bool gpr_only = true; > + bool used_only = true; > + enum zero_call_used_regs zero_call_used_regs_type = > zero_call_used_regs_unset; > + > + if (flag_zero_call_used_regs) > + if (DECL_ZERO_CALL_USED_REGS (current_function_decl) > + == zero_call_used_regs_unset) > + zero_call_used_regs_type = flag_zero_call_used_regs; > + else > + zero_call_used_regs_type > + = DECL_ZERO_CALL_USED_REGS (current_function_decl); > + else > + zero_call_used_regs_type = DECL_ZERO_CALL_USED_REGS > (current_function_decl); > + > + /* No need to zero call-used-regs when no user request is present. */ > + if (zero_call_used_regs_type <= zero_call_used_regs_skip) > + return; > + > + /* No need to zero call-used-regs in main (). */ > + if (MAIN_NAME_P (DECL_NAME (current_function_decl))) > + return; > + > + /* No need to zero call-used-regs if __builtin_eh_return is called > + since it isn't a normal function return. */ > + if (crtl->calls_eh_return) > + return; > + > + /* If gpr_only is true, only zero call-used-registers that are > + general-purpose registers; if used_only is true, only zero > + call-used-registers that are used in the current function. */ > + switch (zero_call_used_regs_type) > + { > + case zero_call_used_regs_all_gpr: > + used_only = false; > + break; > + case zero_call_used_regs_used: > + gpr_only = false; > + break; > + case zero_call_used_regs_all: > + gpr_only = false; > + used_only = false; > + break; > + default: > + break; > + } > + > + /* An optimization to use a single hard insn to zero all vector registers > on > + the target that provides such insn. */ > + if (!gpr_only > + && targetm.calls.zero_all_vector_registers) > + { > + rtx zero_all_vec_insn > + = targetm.calls.zero_all_vector_registers (used_only); > + if (zero_all_vec_insn) > + { > + emit_insn (zero_all_vec_insn); > + gpr_only = true; > + } > + } > + > + /* For each of the hard registers, check to see whether we should zero it > if: > + 1. it is a call-used-registers; > + and 2. it is not a fixed-registers; > + and 3. it is not live at the end of the routine; > + and 4. it is general purpose register if gpr_only is true; > + and 5. it is used in the routine if used_only is true; > + */ > + > + /* This array holds the zero rtx with the correponding machine mode. */ > + rtx zero_rtx[(int)MAX_MACHINE_MODE]; > + for (int i = 0; i < (int) MAX_MACHINE_MODE; i++) > + zero_rtx[i] = NULL_RTX; > + > + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) > + { > + if (!this_target_hard_regs->x_call_used_regs[regno]) > + continue; > + if (fixed_regs[regno]) > + continue; > + if (is_live_reg_at_exit (regno)) > + continue; > + if (!targetm.calls.zero_call_used_regno_p (regno, gpr_only)) > + continue; > + if (used_only && !df_regs_ever_live_p (regno)) > + continue; > + > + /* Now we can emit insn to zero this register. */ > + rtx reg, tmp; > + > + machine_mode mode > + = targetm.calls.zero_call_used_regno_mode (regno, > + reg_raw_mode[regno]); > + if (mode == VOIDmode) > + continue; > + if (!have_regs_of_mode[mode]) > + continue; > + > + reg = gen_rtx_REG (mode, regno); > + if (zero_rtx[(int)mode] == NULL_RTX) > + { > + zero_rtx[(int)mode] = reg; > + tmp = gen_rtx_SET (reg, const0_rtx); > + emit_insn (tmp); > + } > + else > + emit_move_insn (reg, zero_rtx[(int)mode]); > + > + emit_insn (targetm.calls.pro_epilogue_use (reg)); > + } > + > + return; > +} > + > + > /* Return a sequence to be used as the epilogue for the current function, > or NULL. */ > > @@ -5819,6 +5961,9 @@ make_epilogue_seq (void) > > start_sequence (); > emit_note (NOTE_INSN_EPILOGUE_BEG); > + > + gen_call_used_regs_seq (); > + > rtx_insn *seq = targetm.gen_epilogue (); > if (seq) > emit_jump_insn (seq); > diff --git a/gcc/function.h b/gcc/function.h > index d55cbdd..fc36c3e 100644 > --- a/gcc/function.h > +++ b/gcc/function.h > @@ -705,4 +705,6 @@ extern const char *current_function_name (void); > > extern void used_types_insert (tree); > > +extern bool is_live_reg_at_exit (unsigned int); > + > #endif /* GCC_FUNCTION_H */ > diff --git a/gcc/target.def b/gcc/target.def > index 07059a8..8aab63e 100644 > --- a/gcc/target.def > +++ b/gcc/target.def > @@ -5022,6 +5022,26 @@ If this hook is not defined, then > FUNCTION_VALUE_REGNO_P will be used.", > default_function_value_regno_p) > > DEFHOOK > +(zero_call_used_regno_p, > + "A target hook that returns @code{true} if @var{regno} is the number of a\n\ > +call used register. If @var{general_reg_only_p} is @code{true},\n\ > +@var{regno} must be the number of a hard general register.\n\ > +\n\ > +If this hook is not defined, then default_zero_call_used_regno_p will be > used.", > + bool, (const unsigned int regno, bool general_reg_only_p), > + default_zero_call_used_regno_p) > + > +DEFHOOK > +(zero_call_used_regno_mode, > + "A target hook that returns a mode of suitable to zero the register for > the\n\ > +call used register @var{regno} in @var{mode}.\n\ > +\n\ > +If this hook is not defined, then default_zero_call_used_regno_mode will > be\n\ > +used.", > + machine_mode, (const unsigned int regno, machine_mode mode), > + default_zero_call_used_regno_mode) > + > +DEFHOOK > (fntype_abi, > "Return the ABI used by a function with type @var{type}; see the\n\ > definition of @code{predefined_function_abi} for details of the ABI\n\ > @@ -5068,6 +5088,19 @@ argument list due to stack realignment. Return > @code{NULL} if no DRAP\n\ > is needed.", > rtx, (void), NULL) > > +DEFHOOK > +(pro_epilogue_use, > + "This hook should return a UNSPEC_VOLATILE rtx to mark a register in use > to\n\ > +prevent deleting register setting instructions in proprologue and epilogue.", > + rtx, (rtx reg), NULL) > + > +DEFHOOK > +(zero_all_vector_registers, > + "This hook should return an rtx to zero all vector registers at function\n\ > +exit. If @var{used_only} is @code{true}, only used vector registers > should\n\ > +be zeroed. Return @code{NULL} if possible", > + rtx, (bool used_only), NULL) > + > /* Return true if all function parameters should be spilled to the > stack. */ > DEFHOOK > diff --git a/gcc/targhooks.c b/gcc/targhooks.c > index 0113c7b..ed02173 100644 > --- a/gcc/targhooks.c > +++ b/gcc/targhooks.c > @@ -987,6 +987,23 @@ default_function_value_regno_p (const unsigned int regno > ATTRIBUTE_UNUSED) > #endif > } > > +/* The default hook for TARGET_ZERO_CALL_USED_REGNO_P. */ > + > +bool > +default_zero_call_used_regno_p (const unsigned int, > + bool) > +{ > + return false; > +} > + > +/* The default hook for TARGET_ZERO_CALL_USED_REGNO_MODE. */ > + > +machine_mode > +default_zero_call_used_regno_mode (const unsigned int, machine_mode mode) > +{ > + return mode; > +} > + > rtx > default_internal_arg_pointer (void) > { > diff --git a/gcc/targhooks.h b/gcc/targhooks.h > index b572a36..370df19 100644 > --- a/gcc/targhooks.h > +++ b/gcc/targhooks.h > @@ -162,6 +162,9 @@ extern bool hook_bool_const_rtx_commutative_p (const_rtx, > int); > extern rtx default_function_value (const_tree, const_tree, bool); > extern rtx default_libcall_value (machine_mode, const_rtx); > extern bool default_function_value_regno_p (const unsigned int); > +extern bool default_zero_call_used_regno_p (const unsigned int, bool); > +extern machine_mode default_zero_call_used_regno_mode (const unsigned int, > + machine_mode); > extern rtx default_internal_arg_pointer (void); > extern rtx default_static_chain (const_tree, bool); > extern void default_trampoline_init (rtx, tree, rtx); > diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c > b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c > new file mode 100644 > index 0000000..3c2ac72 > --- /dev/null > +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c > @@ -0,0 +1,3 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ > +/* { dg-error "'-fzero-call-used-regs=' is not supported for this target" "" > { target { ! "i?86-*-* x86_64-*-*" } } 0 } */ > diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c > b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c > new file mode 100644 > index 0000000..acf48c4 > --- /dev/null > +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c > @@ -0,0 +1,4 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2" } */ > + > +extern int foo (int) __attribute__ ((zero_call_used_regs("all-gpr"))); /* { > dg-warning " attribute directive ignored" "" {target { ! "i?86-*-* > x86_64-*-*" } } 0 } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c > new file mode 100644 > index 0000000..9f61dc4 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c > @@ -0,0 +1,12 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c > new file mode 100644 > index 0000000..09048e5 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c > @@ -0,0 +1,21 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ > + > +extern int foo (int) __attribute__ ((zero_call_used_regs("all-gpr"))); > + > +int > +foo (int x) > +{ > + return x; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c > new file mode 100644 > index 0000000..4862688 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c > @@ -0,0 +1,39 @@ > +/* { dg-do run { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ > + > +struct S { int i; }; > +__attribute__((const, noinline, noclone)) > +struct S foo (int x) > +{ > + struct S s; > + s.i = x; > + return s; > +} > + > +int a[2048], b[2048], c[2048], d[2048]; > +struct S e[2048]; > + > +__attribute__((noinline, noclone)) void > +bar (void) > +{ > + int i; > + for (i = 0; i < 1024; i++) > + { > + e[i] = foo (i); > + a[i+2] = a[i] + a[i+1]; > + b[10] = b[10] + i; > + c[i] = c[2047 - i]; > + d[i] = d[i + 1]; > + } > +} > + > +int > +main () > +{ > + int i; > + bar (); > + for (i = 0; i < 1024; i++) > + if (e[i].i != i) > + __builtin_abort (); > + return 0; > +} > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c > new file mode 100644 > index 0000000..500251b > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c > @@ -0,0 +1,39 @@ > +/* { dg-do run { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ > + > +struct S { int i; }; > +__attribute__((const, noinline, noclone)) > +struct S foo (int x) > +{ > + struct S s; > + s.i = x; > + return s; > +} > + > +int a[2048], b[2048], c[2048], d[2048]; > +struct S e[2048]; > + > +__attribute__((noinline, noclone)) void > +bar (void) > +{ > + int i; > + for (i = 0; i < 1024; i++) > + { > + e[i] = foo (i); > + a[i+2] = a[i] + a[i+1]; > + b[10] = b[10] + i; > + c[i] = c[2047 - i]; > + d[i] = d[i + 1]; > + } > +} > + > +int > +main () > +{ > + int i; > + bar (); > + for (i = 0; i < 1024; i++) > + if (e[i].i != i) > + __builtin_abort (); > + return 0; > +} > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c > new file mode 100644 > index 0000000..8b058e3 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c > @@ -0,0 +1,21 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7" } */ > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */ > +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { > target { ia32 } } } } */ > +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 15 > { target { ! ia32 } } } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c > new file mode 100644 > index 0000000..d4eaaf7 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c > @@ -0,0 +1,19 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */ > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-times "vzeroall" 1 } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c > new file mode 100644 > index 0000000..dd3bb90 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c > @@ -0,0 +1,14 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ > + > +extern void foo (void) __attribute__ ((zero_call_used_regs("used"))); > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c > new file mode 100644 > index 0000000..e2274f6 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c > @@ -0,0 +1,14 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ > + > +extern void foo (void) __attribute__ ((zero_call_used_regs("skip"))); > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c > new file mode 100644 > index 0000000..7f5d153 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c > @@ -0,0 +1,13 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ > + > +int > +foo (int x) > +{ > + return x; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c > new file mode 100644 > index 0000000..fe13d2b > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c > @@ -0,0 +1,13 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=used -march=corei7" } */ > + > +float > +foo (float z, float y, float x) > +{ > + return x + y; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 > } } } } */ > +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! > ia32 } } } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c > new file mode 100644 > index 0000000..205a532 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c > @@ -0,0 +1,12 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=used -march=corei7" } */ > + > +float > +foo (float z, float y, float x) > +{ > + return x; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm2, %xmm2" { target { ! ia32 > } } } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c > new file mode 100644 > index 0000000..e046684 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c > @@ -0,0 +1,19 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c > new file mode 100644 > index 0000000..4be8ff6 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c > @@ -0,0 +1,23 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7" } */ > + > +float > +foo (float z, float y, float x) > +{ > + return x + y; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" { target { ia32 } > } } } */ > +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 > } } } } */ > +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { > target { ia32 } } } } */ > +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm1, %xmm\[0-9\]+" 14 > { target { ! ia32 } } } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c > new file mode 100644 > index 0000000..0eb34e0 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c > @@ -0,0 +1,14 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=skip -march=corei7" } */ > + > +__attribute__ ((zero_call_used_regs("used"))) > +float > +foo (float z, float y, float x) > +{ > + return x + y; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 > } } } } */ > +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! > ia32 } } } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c > new file mode 100644 > index 0000000..cbb63a4 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c > @@ -0,0 +1,19 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */ > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c > new file mode 100644 > index 0000000..7573197 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c > @@ -0,0 +1,19 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx512f" } */ > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c > new file mode 100644 > index 0000000..de71223 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c > @@ -0,0 +1,12 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c > new file mode 100644 > index 0000000..ccfa441 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c > @@ -0,0 +1,14 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ > + > +extern void foo (void) __attribute__ ((zero_call_used_regs("used-gpr"))); > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c > new file mode 100644 > index 0000000..6b46ca3 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c > @@ -0,0 +1,20 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ > + > +__attribute__ ((zero_call_used_regs("all-gpr"))) > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c > new file mode 100644 > index 0000000..0680f38 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c > @@ -0,0 +1,14 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ > + > +extern void foo (void) __attribute__ ((zero_call_used_regs("skip"))); > + > +void > +foo (void) > +{ > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ > +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c > new file mode 100644 > index 0000000..534defa > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c > @@ -0,0 +1,13 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ > + > +int > +foo (int x) > +{ > + return x; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c > new file mode 100644 > index 0000000..477bb19 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c > @@ -0,0 +1,19 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ > + > +int > +foo (int x) > +{ > + return x; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } > } } } */ > +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } > } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c > b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c > new file mode 100644 > index 0000000..a305a60 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c > @@ -0,0 +1,15 @@ > +/* { dg-do compile { target *-*-linux* } } */ > +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ > + > +extern int foo (int) __attribute__ ((zero_call_used_regs("used-gpr"))); > + > +int > +foo (int x) > +{ > + return x; > +} > + > +/* { dg-final { scan-assembler-not "vzeroall" } } */ > +/* { dg-final { scan-assembler-not "%xmm" } } */ > +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ > +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } > } } } */ > diff --git a/gcc/toplev.c b/gcc/toplev.c > index 95eea63..01a1f24 100644 > --- a/gcc/toplev.c > +++ b/gcc/toplev.c > @@ -1464,6 +1464,15 @@ process_options (void) > } > } > > + if (flag_zero_call_used_regs != zero_call_used_regs_skip > + && !targetm.calls.pro_epilogue_use) > + { > + error_at (UNKNOWN_LOCATION, > + "%<-fzero-call-used-regs=%> is not supported for this " > + "target"); > + flag_zero_call_used_regs = zero_call_used_regs_skip; > + } > + > /* One region RA really helps to decrease the code size. */ > if (flag_ira_region == IRA_REGION_AUTODETECT) > flag_ira_region > diff --git a/gcc/tree-core.h b/gcc/tree-core.h > index 8c5a2e3..71badbd 100644 > --- a/gcc/tree-core.h > +++ b/gcc/tree-core.h > @@ -1825,7 +1825,11 @@ struct GTY(()) tree_decl_with_vis { > unsigned final : 1; > /* Belong to FUNCTION_DECL exclusively. */ > unsigned regdecl_flag : 1; > - /* 14 unused bits. */ > + > + /* How to clear call-used registers upon function return. */ > + ENUM_BITFIELD(zero_call_used_regs) zero_call_used_regs_type : 3; > + > + /* 11 unused bits. */ > }; > > struct GTY(()) tree_var_decl { > diff --git a/gcc/tree.h b/gcc/tree.h > index cf546ed..d378a88 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -2925,6 +2925,11 @@ extern void decl_value_expr_insert (tree, tree); > #define DECL_VISIBILITY(NODE) \ > (DECL_WITH_VIS_CHECK (NODE)->decl_with_vis.visibility) > > +/* Value of the function decl's type of zeroing the call used > + registers upon return from function. */ > +#define DECL_ZERO_CALL_USED_REGS(NODE) \ > + (DECL_WITH_VIS_CHECK (NODE)->decl_with_vis.zero_call_used_regs_type) > + > /* Nonzero means that the decl (or an enclosing scope) had its > visibility specified rather than being inferred. */ > #define DECL_VISIBILITY_SPECIFIED(NODE) \ > -- > 1.9.1