[Patch][Middle-end]Add -fzero-call-used-regs=[skip|used-gpr|all-gpr|used|all]
Qing Zhao
QING.ZHAO@ORACLE.COM
Tue Jul 14 14:45:04 GMT 2020
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.zhao@oracle.com <mailto:qing.zhao@oracle.com>>
2020-07-13 H.J. Lu <hjl.tools@gmail.com <mailto:hjl.tools@gmail.com>>
* common.opt: Add new option -fzero-call-used-regs.
* config/i386/i386.c (ix86_zero_call_used_regno_p): New function.
(ix86_zero_call_used_regno_mode): Likewise.
(ix86_zero_all_vector_registers): Likewise.
(ix86_expand_prologue): Replace gen_prologue_use with
gen_pro_epilogue_use.
(TARGET_ZERO_CALL_USED_REGNO_P): Define.
(TARGET_ZERO_CALL_USED_REGNO_MODE): Define.
(TARGET_PRO_EPILOGUE_USE): Define.
(TARGET_ZERO_ALL_VECTOR_REGISTERS): Define.
* config/i386/i386.md: Replace UNSPECV_PROLOGUE_USE
with UNSPECV_PRO_EPILOGUE_USE.
* coretypes.h (enum zero_call_used_regs): New type.
* doc/extend.texi: Document the new zero_call_used_regs attribute.
* doc/invoke.texi: Document the new -fzero-call-used-regs option.
* doc/tm.texi: Regenerate.
* doc/tm.texi.in (TARGET_ZERO_CALL_USED_REGNO_P): New hook.
(TARGET_ZERO_CALL_USED_REGNO_MODE): Likewise.
(TARGET_PRO_EPILOGUE_USE): Likewise.
(TARGET_ZERO_ALL_VECTOR_REGISTERS): Likewise.
* function.c (is_live_reg_at_exit): New function.
(gen_call_used_regs_seq): Likewise.
(make_epilogue_seq): Call gen_call_used_regs_seq.
* function.h (is_live_reg_at_exit): Declare.
* target.def (zero_call_used_regno_p): New hook.
(zero_call_used_regno_mode): Likewise.
(pro_epilogue_use): Likewise.
(zero_all_vector_registers): Likewise.
* targhooks.c (default_zero_call_used_regno_p): New function.
(default_zero_call_used_regno_mode): Likewise.
* targhooks.h (default_zero_call_used_regno_p): Declare.
(default_zero_call_used_regno_mode): Declare.
* toplev.c (process_options): Issue errors when -fzero-call-used-regs
is used on targets that do not support it.
* tree-core.h (struct tree_decl_with_vis): New field
zero_call_used_regs_type.
* tree.h (DECL_ZERO_CALL_USED_REGS): New macro.
gcc/c-family/ChangeLog:
2020-07-13 qing zhao <qing.zhao@oracle.com <mailto:qing.zhao@oracle.com>>
2020-07-13 H.J. Lu <hjl.tools@gmail.com <mailto:hjl.tools@gmail.com>>
* c-attribs.c (c_common_attribute_table): Add new attribute
zero_call_used_regs.
(handle_zero_call_used_regs_attribute): New function.
gcc/c/ChangeLog:
2020-07-13 qing zhao <qing.zhao@oracle.com <mailto:qing.zhao@oracle.com>>
2020-07-13 H.J. Lu <hjl.tools@gmail.com <mailto:hjl.tools@gmail.com>>
* c-decl.c (merge_decls): Merge zero_call_used_regs_type.
gcc/testsuite/ChangeLog:
2020-07-13 qing zhao <qing.zhao@oracle.com <mailto:qing.zhao@oracle.com>>
2020-07-13 H.J. Lu <hjl.tools@gmail.com <mailto:hjl.tools@gmail.com>>
* c-c++-common/zero-scratch-regs-1.c: New test.
* c-c++-common/zero-scratch-regs-2.c: Likewise.
* gcc.target/i386/zero-scratch-regs-1.c: Likewise.
* gcc.target/i386/zero-scratch-regs-10.c: Likewise.
* gcc.target/i386/zero-scratch-regs-11.c: Likewise.
* gcc.target/i386/zero-scratch-regs-12.c: Likewise.
* gcc.target/i386/zero-scratch-regs-13.c: Likewise.
* gcc.target/i386/zero-scratch-regs-14.c: Likewise.
* gcc.target/i386/zero-scratch-regs-15.c: Likewise.
* gcc.target/i386/zero-scratch-regs-16.c: Likewise.
* gcc.target/i386/zero-scratch-regs-17.c: Likewise.
* gcc.target/i386/zero-scratch-regs-18.c: Likewise.
* gcc.target/i386/zero-scratch-regs-19.c: Likewise.
* gcc.target/i386/zero-scratch-regs-2.c: Likewise.
* gcc.target/i386/zero-scratch-regs-20.c: Likewise.
* gcc.target/i386/zero-scratch-regs-21.c: Likewise.
* gcc.target/i386/zero-scratch-regs-22.c: Likewise.
* gcc.target/i386/zero-scratch-regs-23.c: Likewise.
* gcc.target/i386/zero-scratch-regs-3.c: Likewise.
* gcc.target/i386/zero-scratch-regs-4.c: Likewise.
* gcc.target/i386/zero-scratch-regs-5.c: Likewise.
* gcc.target/i386/zero-scratch-regs-6.c: Likewise.
* gcc.target/i386/zero-scratch-regs-7.c: Likewise.
* gcc.target/i386/zero-scratch-regs-8.c: Likewise.
* gcc.target/i386/zero-scratch-regs-9.c: Likewise.
---
gcc/c-family/c-attribs.c | 68 ++++++++++
gcc/c/c-decl.c | 4 +
gcc/common.opt | 23 ++++
gcc/config/i386/i386.c | 58 ++++++++-
gcc/config/i386/i386.md | 6 +-
gcc/coretypes.h | 10 ++
gcc/doc/extend.texi | 11 ++
gcc/doc/invoke.texi | 13 +-
gcc/doc/tm.texi | 27 ++++
gcc/doc/tm.texi.in | 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
More information about the Gcc-patches
mailing list