Hi, This is the 6th version of the implementation of patch -fzero-call-used-regs.
The X86 part is exactly the same as 5th version. (And it has been approved by Uros already). The major change compared to the previous version (5th version) are: 1. Documentation change per Richard’s suggestion; 2. Other minor changes. 3. general testing cases update per Richard’s suggestion; I have tested this new GCC on both x86 and arm64, no regression. Richard, Please let me know whether it’s ready for stage 1 gcc11? thanks. Qing In order to make it easier for you to review the change, I list the changes I added compared to the 5th version first, and then the whole patch followed. *****The patch compared to 5th version: From 3545cc92b327e11af0dde832a60161da92cc4262 Mon Sep 17 00:00:00 2001 From: qing zhao <qinz...@gcc.gnu.org> Date: Thu, 29 Oct 2020 18:40:27 +0100 Subject: [PATCH] fix all issues raised by Richard on 10/29 --- gcc/c-family/c-attribs.c | 8 +- gcc/common.opt | 2 +- gcc/doc/extend.texi | 72 ++++++++--------- gcc/doc/invoke.texi | 6 +- gcc/emit-rtl.h | 3 - gcc/flag-types.h | 2 +- gcc/function.c | 56 +++++++------- gcc/opts.c | 2 +- gcc/targhooks.c | 2 +- gcc/testsuite/c-c++-common/zero-scratch-regs-11.c | 90 +--------------------- gcc/testsuite/c-c++-common/zero-scratch-regs-2.c | 13 +--- gcc/testsuite/c-c++-common/zero-scratch-regs-3.c | 13 +--- gcc/testsuite/c-c++-common/zero-scratch-regs-4.c | 13 +--- gcc/testsuite/c-c++-common/zero-scratch-regs-5.c | 13 +--- gcc/testsuite/c-c++-common/zero-scratch-regs-6.c | 13 +--- gcc/testsuite/c-c++-common/zero-scratch-regs-7.c | 13 +--- gcc/testsuite/c-c++-common/zero-scratch-regs-8.c | 13 +--- gcc/testsuite/c-c++-common/zero-scratch-regs-9.c | 13 +--- .../c-c++-common/zero-scratch-regs-attr-usages.c | 10 ++- 19 files changed, 90 insertions(+), 267 deletions(-) diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 1b05e8c..8da1dc7 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -4979,13 +4979,15 @@ handle_zero_call_used_regs_attribute (tree *node, tree name, tree args, error_at (DECL_SOURCE_LOCATION (decl), "%qE attribute applies only to functions", name); *no_add_attrs = true; + return NULL_TREE; } if (TREE_CODE (id) != STRING_CST) { error_at (DECL_SOURCE_LOCATION (decl), - "attribute %qE arguments not a string", name); + "%qE argument not a string", name); *no_add_attrs = true; + return NULL_TREE; } bool found = false; @@ -5000,8 +5002,8 @@ handle_zero_call_used_regs_attribute (tree *node, tree name, tree args, if (!found) { error_at (DECL_SOURCE_LOCATION (decl), - "unrecognized zero_call_used_regs attribute: %qs", - TREE_STRING_POINTER (id)); + "unrecognized %qE attribute argument %qs", + name, TREE_STRING_POINTER (id)); *no_add_attrs = true; } diff --git a/gcc/common.opt b/gcc/common.opt index 4a13f32..d716ea1 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -228,7 +228,7 @@ unsigned int flag_sanitize_coverage Variable bool dump_base_name_prefixed = false -; What subset of registers should be zeroed +; What subset of registers should be zeroed on function return Variable unsigned int flag_zero_call_used_regs diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index b011c17..25b3909 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3996,22 +3996,19 @@ with a named @code{target} must be @code{static}. @cindex @code{zero_call_used_regs} function attribute The @code{zero_call_used_regs} attribute causes the compiler to zero -a subset of all call-used registers at function return according to -@var{choice}. -This is used to increase the program security by either mitigating -Return-Oriented Programming (ROP) or preventing information leak +a subset of all call-used registers@footnote{A ``call-used'' register +is a register whose contents can be changed by a function call; +therefore, a caller cannot assume that the register has the same contents +on return from the function as it had before calling the function. Such +registers are also called ``call-clobbered'', ``caller-saved'', or +``volatile''.} at function return. +This is used to increase program security by either mitigating +Return-Oriented Programming (ROP) attacks or preventing information leakage through registers. -A ``call-used'' register is a register whose contents can be changed by -a function call; therefore, a caller cannot assume that the register has -the same contents on return from the function as it had before calling -the function. Such registers are also called ``call-clobbered'', -``caller-saved'', or ``volatile''. - In order to satisfy users with different security needs and control the -run-time overhead at the same time, GCC provides a flexible way to choose -the subset of the call-used registers to be zeroed. - +run-time overhead at the same time, @var{choice} parameter provides a +flexible way to choose the subset of the call-used registers to be zeroed. The three basic values of @var{choice} are: @itemize @bullet @@ -4046,42 +4043,41 @@ together, they must appear in the order above. The full list of @var{choice}s is therefore: -@itemize @bullet -@item -@samp{skip} doesn't zero any call-used register. +@table @code +@item skip +doesn't zero any call-used register. -@item -@samp{used} only zeros call-used registers that are used in the function. +@item used +only zeros call-used registers that are used in the function. -@item -@samp{all} zeros all call-used registers. +@item used-gpr +only zeros call-used general purpose registers that are used in the function. -@item -@samp{used-arg} only zeros used call-used registers that pass arguments. +@item used-arg +only zeros call-used registers that are used in the function and pass arguments. -@item -@samp{used-gpr} only zeros used call-used general purpose registers. +@item used-gpr-arg +only zeros call-used general purpose registers that are used in the function +and pass arguments. -@item -@samp{used-gpr-arg} only zeros used call-used general purpose registers that -pass arguments. +@item all +zeros all call-used registers. -@item -@samp{all-gpr-arg} zeros all call-used general purpose registers that pass -arguments. +@item all-gpr +zeros all call-used general purpose registers. -@item -@samp{all-arg} zeros all call-used registers that pass arguments. +@item all-arg +zeros all call-used registers that pass arguments. -@item -@samp{all-gpr} zeros all call-used general purpose registers. -@end itemize +@item all-gpr-arg +zeros all call-used general purpose registers that pass +arguments. +@end table -Among this list, @samp{used-gpr-arg}, @samp{used-arg}, @samp{all-gpr-arg}, -and @samp{all-arg} are mainly used for ROP mitigation. +Of this list, @samp{used-arg}, @samp{used-gpr-arg}, @samp{all-arg}, +and @samp{all-gpr-arg} are mainly used for ROP mitigation. The default for the attribute is controlled by @option{-fzero-call-used-regs}. - @end table @c This is the end of the target-independent attribute table diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index c9e3128..23834a2 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -12552,9 +12552,9 @@ Not all targets support this option. @item -fzero-call-used-regs=@var{choice} @opindex fzero-call-used-regs -Zero call-used registers at function return to increase the program -security by either mitigating Return-Oriented Programming (ROP) or -preventing information leak through registers. +Zero call-used registers at function return to increase program +security by either mitigating Return-Oriented Programming (ROP) +attacks or preventing information leakage through registers. The possible values of @var{choice} are the same as for the @code{zero_call_used_regs} attribute (@pxref{Function Attributes}). diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h index d7bdb66..4658c4a 100644 --- a/gcc/emit-rtl.h +++ b/gcc/emit-rtl.h @@ -173,9 +173,6 @@ struct GTY(()) rtl_data { local stack. */ unsigned int stack_alignment_estimated; - /* How to zero call-used regsiters for this routine. */ - unsigned int zero_call_used_regs; - /* How many NOP insns to place at each function entry by default. */ unsigned short patch_area_size; diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 8807635..51c7e5f 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -288,7 +288,7 @@ enum sanitize_code { }; /* Different settings for zeroing subset of registers. */ -namespace zero_regs_code { +namespace zero_regs_flag { const unsigned int UNSET = 0; const unsigned int SKIP = 1UL << 0; const unsigned int ONLY_USED = 1UL << 1; diff --git a/gcc/function.c b/gcc/function.c index 77f0b5d..76a5037 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -5817,15 +5817,15 @@ make_prologue_seq (void) return seq; } -/* Emit a sequence of insns to zero the call-used registers before RET. */ -using namespace zero_regs_code; +/* Emit a sequence of insns to zero the call-used registers before RET + according to ZERO_REGS_TYPE. */ static void -gen_call_used_regs_seq (rtx_insn *ret) +gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type) { - bool gpr_only = true; - bool used_only = true; - bool arg_only = true; + bool only_gpr = true; + bool only_used = true; + bool only_arg = true; /* No need to zero call-used-regs in main (). */ if (MAIN_NAME_P (DECL_NAME (current_function_decl))) @@ -5836,23 +5836,25 @@ gen_call_used_regs_seq (rtx_insn *ret) 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 + /* If only_gpr is true, only zero call-used registers that are + general-purpose registers; if only_used is true, only zero call-used registers that are used in the current function; - if arg_only is true, only zero call-used registers that pass + if only_arg is true, only zero call-used registers that pass parameters defined by the flatform's calling conversion. */ - gpr_only = crtl->zero_call_used_regs & ONLY_GPR; - used_only = crtl->zero_call_used_regs & ONLY_USED; - arg_only = crtl->zero_call_used_regs & ONLY_ARG; + using namespace zero_regs_flag; + + only_gpr = zero_regs_type & ONLY_GPR; + only_used = zero_regs_type & ONLY_USED; + only_arg = zero_regs_type & ONLY_ARG; /* For each of the hard registers, we should zero it if: 1. it is a call-used register; and 2. it is not a fixed register; and 3. it is not live at the return of the routine; - and 4. it is general registor if gpr_only is true; - and 5. it is used in the routine if used_only is true; - and 6. it is a register that passes parameter if arg_only is true. */ + and 4. it is general registor if only_gpr is true; + and 5. it is used in the routine if only_used is true; + and 6. it is a register that passes parameter if only_arg is true. */ /* First, prepare the data flow information. */ basic_block bb = BLOCK_FOR_INSN (ret); @@ -5871,12 +5873,12 @@ gen_call_used_regs_seq (rtx_insn *ret) continue; if (REGNO_REG_SET_P (live_out, regno)) continue; - if (gpr_only + if (only_gpr && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno)) continue; - if (used_only && !df_regs_ever_live_p (regno)) + if (only_used && !df_regs_ever_live_p (regno)) continue; - if (arg_only && !FUNCTION_ARG_REGNO_P (regno)) + if (only_arg && !FUNCTION_ARG_REGNO_P (regno)) continue; /* Now this is a register that we might want to zero. */ @@ -6614,14 +6616,14 @@ public: unsigned int pass_zero_call_used_regs::execute (function *fun) { + using namespace zero_regs_flag; unsigned int zero_regs_type = UNSET; - unsigned int attr_zero_regs_type = UNSET; - tree attr_zero_regs - = lookup_attribute ("zero_call_used_regs", - DECL_ATTRIBUTES (fun->decl)); + tree attr_zero_regs = lookup_attribute ("zero_call_used_regs", + DECL_ATTRIBUTES (fun->decl)); - /* Get the type of zero_call_used_regs from function attribute. */ + /* Get the type of zero_call_used_regs from function attribute. + We have filtered out invalid attribute values already at this point. */ if (attr_zero_regs) { /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE @@ -6635,18 +6637,14 @@ pass_zero_call_used_regs::execute (function *fun) if (strcmp (TREE_STRING_POINTER (attr_zero_regs), zero_call_used_regs_opts[i].name) == 0) { - attr_zero_regs_type = zero_call_used_regs_opts[i].flag; + zero_regs_type = zero_call_used_regs_opts[i].flag; break; } } - zero_regs_type = attr_zero_regs_type; - if (!zero_regs_type) zero_regs_type = flag_zero_call_used_regs; - crtl->zero_call_used_regs = zero_regs_type; - /* No need to zero call-used-regs when no user request is present. */ if (!(zero_regs_type & ENABLED)) return 0; @@ -6664,7 +6662,7 @@ pass_zero_call_used_regs::execute (function *fun) { rtx_insn *insn = BB_END (e->src); if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn))) - gen_call_used_regs_seq (insn); + gen_call_used_regs_seq (insn, zero_regs_type); } return 0; diff --git a/gcc/opts.c b/gcc/opts.c index 7cd097a..8c439e4 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1776,7 +1776,7 @@ const struct sanitizer_opts_s coverage_sanitizer_opts[] = { NULL, 0U, 0UL, false } }; -using namespace zero_regs_code; +using namespace zero_regs_flag; /* -fzero-call-used-regs= suboptions. */ const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] = { diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 88eef00..4e4d100 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1011,7 +1011,7 @@ default_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs) { issued_error = true; sorry ("%qs not supported on this target", - "fzero-call-used_regs"); + "-fzero-call-used_regs"); } delete_insns_since (last_insn); } diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c index 6756f57..7721e39 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c @@ -1,92 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=all" } */ -#include <assert.h> -int result = 0; - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("skip"))) -foo1 (int x) -{ - return (x + 1); -} - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("used-gpr-arg"))) -foo2 (int x) -{ - return (x + 2); -} - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("used-gpr"))) -foo3 (int x) -{ - return (x + 3); -} - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("used-arg"))) -foo4 (int x) -{ - return (x + 4); -} - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("used"))) -foo5 (int x) -{ - return (x + 5); -} - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("all-gpr-arg"))) -foo6 (int x) -{ - return (x + 6); -} - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("all-gpr"))) -foo7 (int x) -{ - return (x + 7); -} - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("all-arg"))) -foo8 (int x) -{ - return (x + 8); -} - -int -__attribute__((noipa)) -__attribute__ ((zero_call_used_regs("all"))) -foo9 (int x) -{ - return (x + 9); -} - -int main() -{ - result = foo1 (1); - result += foo2 (1); - result += foo3 (1); - result += foo4 (1); - result += foo5 (1); - result += foo6 (1); - result += foo7 (1); - result += foo8 (1); - result += foo9 (1); - assert (result == 54); - return 0; -} +#include "zero-scratch-regs-10.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c index 73c3794..25891ac 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c @@ -1,15 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */ -volatile int result = 0; -int -__attribute__((noipa)) -foo (int x) -{ - return x; -} -int main() -{ - result = foo (2); - return 0; -} +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c index d3db757..7c3d286 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c @@ -1,15 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ -volatile int result = 0; -int -__attribute__((noipa)) -foo (int x) -{ - return x; -} -int main() -{ - result = foo (2); - return 0; -} +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c index 029aa79..ba28c06 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c @@ -1,15 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */ -volatile int result = 0; -int -__attribute__((noipa)) -foo (int x) -{ - return x; -} -int main() -{ - result = foo (2); - return 0; -} +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c index 509ce20..26679a4 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c @@ -1,15 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=used" } */ -volatile int result = 0; -int -__attribute__((noipa)) -foo (int x) -{ - return x; -} -int main() -{ - result = foo (2); - return 0; -} +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c index 56d8063..80f5bbb 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c @@ -1,15 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */ -volatile int result = 0; -int -__attribute__((noipa)) -foo (int x) -{ - return x; -} -int main() -{ - result = foo (2); - return 0; -} +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c index 40378ed..159f35c 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c @@ -1,15 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ -volatile int result = 0; -int -__attribute__((noipa)) -foo (int x) -{ - return x; -} -int main() -{ - result = foo (2); - return 0; -} +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c index 7bdef95..c1faaf0 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c @@ -1,15 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */ -volatile int result = 0; -int -__attribute__((noipa)) -foo (int x) -{ - return x; -} -int main() -{ - result = foo (2); - return 0; -} +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c index 032da1a..3f14bac 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c @@ -1,15 +1,4 @@ /* { dg-do run } */ /* { dg-options "-O2 -fzero-call-used-regs=all" } */ -volatile int result = 0; -int -__attribute__((noipa)) -foo (int x) -{ - return x; -} -int main() -{ - result = foo (2); - return 0; -} +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c index c60e946..1e75795 100644 --- a/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c @@ -1,10 +1,16 @@ -/* { dg-do compile} */ +/* { dg-do compile } */ /* { dg-options "-O2" } */ int result __attribute__ ((zero_call_used_regs("all"))); /* { dg-error "attribute applies only to functions" } */ int __attribute__ ((zero_call_used_regs("gpr-arg-all"))) -foo1 (int x) /* { dg-error "unrecognized zero_call_used_regs attribute" } */ +foo1 (int x) /* { dg-error "unrecognized 'zero_call_used_regs' attribute argument" } */ { return (x + 1); } +int +__attribute__ ((zero_call_used_regs(1))) +foo2 (int x) /* { dg-error "argument not a string" } */ +{ + return (x + 2); +} -- 1.8.3.1 ******The whole patch: From bc91029c821ed8c3aad2023375e2ab44b0f33166 Mon Sep 17 00:00:00 2001 From: qing zhao <qing.z...@oracle.com> Date: Thu, 1 Oct 2020 00:27:57 +0000 Subject: [PATCH] The 6th version of -fzero-call-used-regs We will provide a new feature into GCC: Add -fzero-call-used-regs=[skip|used-gpr-arg|used-arg|all-gpr-arg |all-arg|used-gpr|all-gpr|used|all] command-line option and zero_call_used_regs("skip|used-gpr-arg|used-arg|all-gpr-arg |all-arg|used-gpr|all-gpr|used|all") function attribues: 1. -fzero-call-used-regs=skip and zero_call_used_regs("skip") 2. -fzero-call-used-regs=used-gpr-arg and zero_call_used_regs("used-gpr-arg") 3. -fzero-call-used-regs=used-arg and zero_call_used_regs("used-arg") 4. -fzero-call-used-regs=all-gpr-arg and zero_call_used_regs("all-gpr-arg") 5. -fzero-call-used-regs=all-arg and zero_call_used_regs("all-arg") 6. -fzero-call-used-regs=used-gpr and zero_call_used_regs("used-gpr") 7. -fzero-call-used-regs=all-gpr and zero_call_used_regs("all-gpr") 8. -fzero-call-used-regs=used and zero_call_used_regs("used") 9. -fzero-call-used-regs=all and zero_call_used_regs("all") --- gcc/c-family/c-attribs.c | 51 ++++ gcc/common.opt | 8 + gcc/config/i386/i386.c | 294 +++++++++++++++++++++ gcc/df-scan.c | 12 +- gcc/df.h | 1 + gcc/doc/extend.texi | 86 ++++++ gcc/doc/invoke.texi | 15 +- gcc/doc/tm.texi | 12 + gcc/doc/tm.texi.in | 2 + gcc/emit-rtl.h | 3 + gcc/flag-types.h | 22 ++ gcc/function.c | 190 ++++++++++++- gcc/optabs.c | 42 +++ gcc/optabs.h | 2 + gcc/opts.c | 45 ++++ gcc/opts.h | 6 + gcc/passes.def | 1 + gcc/recog.c | 18 +- gcc/recog.h | 1 + gcc/resource.c | 2 +- gcc/target.def | 15 ++ gcc/targhooks.c | 32 +++ gcc/targhooks.h | 1 + gcc/testsuite/c-c++-common/zero-scratch-regs-1.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-10.c | 92 +++++++ gcc/testsuite/c-c++-common/zero-scratch-regs-11.c | 4 + gcc/testsuite/c-c++-common/zero-scratch-regs-2.c | 4 + gcc/testsuite/c-c++-common/zero-scratch-regs-3.c | 4 + gcc/testsuite/c-c++-common/zero-scratch-regs-4.c | 4 + gcc/testsuite/c-c++-common/zero-scratch-regs-5.c | 4 + gcc/testsuite/c-c++-common/zero-scratch-regs-6.c | 4 + gcc/testsuite/c-c++-common/zero-scratch-regs-7.c | 4 + gcc/testsuite/c-c++-common/zero-scratch-regs-8.c | 4 + gcc/testsuite/c-c++-common/zero-scratch-regs-9.c | 4 + .../c-c++-common/zero-scratch-regs-attr-usages.c | 16 ++ .../gcc.target/i386/zero-scratch-regs-1.c | 12 + .../gcc.target/i386/zero-scratch-regs-10.c | 21 ++ .../gcc.target/i386/zero-scratch-regs-11.c | 39 +++ .../gcc.target/i386/zero-scratch-regs-12.c | 39 +++ .../gcc.target/i386/zero-scratch-regs-13.c | 21 ++ .../gcc.target/i386/zero-scratch-regs-14.c | 19 ++ .../gcc.target/i386/zero-scratch-regs-15.c | 14 + .../gcc.target/i386/zero-scratch-regs-16.c | 14 + .../gcc.target/i386/zero-scratch-regs-17.c | 13 + .../gcc.target/i386/zero-scratch-regs-18.c | 13 + .../gcc.target/i386/zero-scratch-regs-19.c | 12 + .../gcc.target/i386/zero-scratch-regs-2.c | 19 ++ .../gcc.target/i386/zero-scratch-regs-20.c | 23 ++ .../gcc.target/i386/zero-scratch-regs-21.c | 14 + .../gcc.target/i386/zero-scratch-regs-22.c | 21 ++ .../gcc.target/i386/zero-scratch-regs-23.c | 29 ++ .../gcc.target/i386/zero-scratch-regs-24.c | 10 + .../gcc.target/i386/zero-scratch-regs-25.c | 10 + .../gcc.target/i386/zero-scratch-regs-26.c | 23 ++ .../gcc.target/i386/zero-scratch-regs-27.c | 15 ++ .../gcc.target/i386/zero-scratch-regs-28.c | 16 ++ .../gcc.target/i386/zero-scratch-regs-29.c | 10 + .../gcc.target/i386/zero-scratch-regs-3.c | 12 + .../gcc.target/i386/zero-scratch-regs-30.c | 12 + .../gcc.target/i386/zero-scratch-regs-31.c | 12 + .../gcc.target/i386/zero-scratch-regs-4.c | 14 + .../gcc.target/i386/zero-scratch-regs-5.c | 20 ++ .../gcc.target/i386/zero-scratch-regs-6.c | 14 + .../gcc.target/i386/zero-scratch-regs-7.c | 13 + .../gcc.target/i386/zero-scratch-regs-8.c | 19 ++ .../gcc.target/i386/zero-scratch-regs-9.c | 15 ++ gcc/tree-pass.h | 1 + 67 files changed, 1552 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-10.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-11.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-2.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-3.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-4.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-5.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-6.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-7.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-8.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-9.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.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-30.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index c779d13..8da1dc7 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -138,6 +138,8 @@ static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *); static tree handle_optimize_attribute (tree *, tree, tree, int, bool *); static tree ignore_attribute (tree *, tree, tree, int, bool *); static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *); +static tree handle_zero_call_used_regs_attribute (tree *, tree, tree, int, + bool *); static tree handle_argspec_attribute (tree *, tree, tree, int, bool *); static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *); static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *); @@ -437,6 +439,8 @@ const struct attribute_spec c_common_attribute_table[] = ignore_attribute, NULL }, { "no_split_stack", 0, 0, true, false, false, false, handle_no_split_stack_attribute, NULL }, + { "zero_call_used_regs", 1, 1, true, false, false, false, + handle_zero_call_used_regs_attribute, NULL }, /* For internal use only (marking of function arguments). The name contains a space to prevent its usage in source code. */ { "arg spec", 1, -1, true, false, false, false, @@ -4959,6 +4963,53 @@ handle_no_split_stack_attribute (tree *node, tree name, return NULL_TREE; } +/* Handle a "zero_call_used_regs" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_zero_call_used_regs_attribute (tree *node, tree name, tree args, + int ARG_UNUSED (flags), + bool *no_add_attrs) +{ + tree decl = *node; + tree id = TREE_VALUE (args); + + if (TREE_CODE (decl) != FUNCTION_DECL) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute applies only to functions", name); + *no_add_attrs = true; + return NULL_TREE; + } + + if (TREE_CODE (id) != STRING_CST) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE argument not a string", name); + *no_add_attrs = true; + return NULL_TREE; + } + + bool found = false; + for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i) + if (strcmp (TREE_STRING_POINTER (id), + zero_call_used_regs_opts[i].name) == 0) + { + found = true; + break; + } + + if (!found) + { + error_at (DECL_SOURCE_LOCATION (decl), + "unrecognized %qE attribute argument %qs", + name, TREE_STRING_POINTER (id)); + *no_add_attrs = true; + } + + return NULL_TREE; +} + /* Handle a "returns_nonnull" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/common.opt b/gcc/common.opt index 292c2de..d716ea1 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -228,6 +228,10 @@ unsigned int flag_sanitize_coverage Variable bool dump_base_name_prefixed = false +; What subset of registers should be zeroed on function return +Variable +unsigned int flag_zero_call_used_regs + ### Driver @@ -3111,6 +3115,10 @@ fzero-initialized-in-bss Common Report Var(flag_zero_initialized_in_bss) Init(1) Put zero initialized data in the bss section. +fzero-call-used-regs= +Common Report RejectNegative Joined +Clear call-used registers upon function return. + g Common Driver RejectNegative JoinedOrMissing Generate debug information in default format. diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index f684954..4360595 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -3551,6 +3551,297 @@ ix86_function_value_regno_p (const unsigned int regno) return false; } +/* Check whether the register REGNO should be zeroed on X86. + When ALL_SSE_ZEROED is true, all SSE registers have been zeroed + together, no need to zero it again. + When NEED_ZERO_MMX is true, MMX registers should be cleared. */ + +static bool +zero_call_used_regno_p (const unsigned int regno, + bool all_sse_zeroed, + bool need_zero_mmx) +{ + return GENERAL_REGNO_P (regno) + || (!all_sse_zeroed && SSE_REGNO_P (regno)) + || MASK_REGNO_P (regno) + || (need_zero_mmx && MMX_REGNO_P (regno)); +} + +/* Return the machine_mode that is used to zero register REGNO. */ + +static machine_mode +zero_call_used_regno_mode (const unsigned int regno) +{ + /* NB: We only need to zero the lower 32 bits for integer registers + and the lower 128 bits for vector registers since destination are + zero-extended to the full register width. */ + if (GENERAL_REGNO_P (regno)) + return SImode; + else if (SSE_REGNO_P (regno)) + return V4SFmode; + else if (MASK_REGNO_P (regno)) + return HImode; + else if (MMX_REGNO_P (regno)) + return V4HImode; + else + gcc_unreachable (); +} + +/* Generate a rtx to zero all vector registers together if possible, + otherwise, return NULL. */ + +static rtx +zero_all_vector_registers (HARD_REG_SET need_zeroed_hardregs) +{ + if (!TARGET_AVX) + return NULL; + + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG) + || (TARGET_64BIT + && (REX_SSE_REGNO_P (regno) + || (TARGET_AVX512F && EXT_REX_SSE_REGNO_P (regno))))) + && !TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + return NULL; + + return gen_avx_vzeroall (); +} + +/* Generate insns to zero all st registers together. + Return true when zeroing instructions are generated. + Assume the number of st registers that are zeroed is num_of_st, + we will emit the following sequence to zero them together: + fldz; \ + fldz; \ + ... + fldz; \ + fstp %%st(0); \ + fstp %%st(0); \ + ... + fstp %%st(0); + i.e., num_of_st fldz followed by num_of_st fstp to clear the stack + mark stack slots empty. + + How to compute the num_of_st: + There is no direct mapping from stack registers to hard register + numbers. If one stack register needs to be cleared, we don't know + where in the stack the value remains. So, if any stack register + needs to be cleared, the whole stack should be cleared. However, + x87 stack registers that hold the return value should be excluded. + x87 returns in the top (two for complex values) register, so + num_of_st should be 7/6 when x87 returns, otherwise it will be 8. */ + + +static bool +zero_all_st_registers (HARD_REG_SET need_zeroed_hardregs) +{ + unsigned int num_of_st = 0; + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((STACK_REGNO_P (regno) || MMX_REGNO_P (regno)) + && TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + { + num_of_st++; + break; + } + + if (num_of_st == 0) + return false; + + bool return_with_x87 = false; + return_with_x87 = (crtl->return_rtx + && (STACK_REG_P (crtl->return_rtx))); + + bool complex_return = false; + complex_return = (crtl->return_rtx + && COMPLEX_MODE_P (GET_MODE (crtl->return_rtx))); + + if (return_with_x87) + if (complex_return) + num_of_st = 6; + else + num_of_st = 7; + else + num_of_st = 8; + + rtx st_reg = gen_rtx_REG (XFmode, FIRST_STACK_REG); + for (unsigned int i = 0; i < num_of_st; i++) + emit_insn (gen_rtx_SET (st_reg, CONST0_RTX (XFmode))); + + for (unsigned int i = 0; i < num_of_st; i++) + { + rtx insn; + insn = emit_insn (gen_rtx_SET (st_reg, st_reg)); + add_reg_note (insn, REG_DEAD, st_reg); + } + return true; +} + + +/* When the routine exit in MMX mode, if any ST register needs + to be zeroed, we should clear all MMX registers except the + RET_MMX_REGNO that holds the return value. */ +static bool +zero_all_mm_registers (HARD_REG_SET need_zeroed_hardregs, + unsigned int ret_mmx_regno) +{ + bool need_zero_all_mm = false; + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (STACK_REGNO_P (regno) + && TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + { + need_zero_all_mm = true; + break; + } + + if (!need_zero_all_mm) + return false; + + rtx zero_mmx = NULL_RTX; + machine_mode mode = V4HImode; + for (unsigned int regno = FIRST_MMX_REG; regno <= LAST_MMX_REG; regno++) + if (regno != ret_mmx_regno) + { + rtx reg = gen_rtx_REG (mode, regno); + if (zero_mmx == NULL_RTX) + { + zero_mmx = reg; + emit_insn (gen_rtx_SET (reg, CONST0_RTX (mode))); + } + else + emit_move_insn (reg, zero_mmx); + } + return true; +} + +/* TARGET_ZERO_CALL_USED_REGS. */ +/* Generate a sequence of instructions that zero registers specified by + NEED_ZEROED_HARDREGS. Return the ZEROED_HARDREGS that are actually + zeroed. */ +static HARD_REG_SET +ix86_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs) +{ + HARD_REG_SET zeroed_hardregs; + bool all_sse_zeroed = false; + bool all_st_zeroed = false; + bool all_mm_zeroed = false; + + CLEAR_HARD_REG_SET (zeroed_hardregs); + + /* first, let's see whether we can zero all vector registers together. */ + rtx zero_all_vec_insn = zero_all_vector_registers (need_zeroed_hardregs); + if (zero_all_vec_insn) + { + emit_insn (zero_all_vec_insn); + all_sse_zeroed = true; + } + + /* mm/st registers are shared registers set, we should follow the following + rules to clear them: + MMX exit mode x87 exit mode + -------------|----------------------|--------------- + uses x87 reg | clear all MMX | clear all x87 + uses MMX reg | clear individual MMX | clear all x87 + x87 + MMX | clear all MMX | clear all x87 + + first, we should decide which mode (MMX mode or x87 mode) the function + exit with. */ + + bool exit_with_mmx_mode = (crtl->return_rtx + && (MMX_REG_P (crtl->return_rtx))); + + if (!exit_with_mmx_mode) + /* x87 exit mode, we should zero all st registers together. */ + { + all_st_zeroed = zero_all_st_registers (need_zeroed_hardregs); + if (all_st_zeroed) + SET_HARD_REG_BIT (zeroed_hardregs, FIRST_STACK_REG); + } + else + /* MMX exit mode, check whether we can zero all mm registers. */ + { + unsigned int exit_mmx_regno = REGNO (crtl->return_rtx); + all_mm_zeroed = zero_all_mm_registers (need_zeroed_hardregs, + exit_mmx_regno); + if (all_mm_zeroed) + for (unsigned int regno = FIRST_MMX_REG; regno <= LAST_MMX_REG; regno++) + if (regno != exit_mmx_regno) + SET_HARD_REG_BIT (zeroed_hardregs, regno); + } + + /* Now, generate instructions to zero all the other registers. */ + + rtx zero_gpr = NULL_RTX; + rtx zero_vector = NULL_RTX; + rtx zero_mask = NULL_RTX; + rtx zero_mmx = NULL_RTX; + + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (!TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + continue; + if (!zero_call_used_regno_p (regno, all_sse_zeroed, + exit_with_mmx_mode && !all_mm_zeroed)) + continue; + + SET_HARD_REG_BIT (zeroed_hardregs, regno); + + rtx reg, tmp, zero_rtx; + machine_mode mode = zero_call_used_regno_mode (regno); + + reg = gen_rtx_REG (mode, regno); + zero_rtx = CONST0_RTX (mode); + + if (mode == SImode) + if (zero_gpr == NULL_RTX) + { + zero_gpr = reg; + tmp = gen_rtx_SET (reg, zero_rtx); + if (!TARGET_USE_MOV0 || optimize_insn_for_size_p ()) + { + rtx clob = gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (CCmode, + FLAGS_REG)); + tmp = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, + tmp, + clob)); + } + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_gpr); + else if (mode == V4SFmode) + if (zero_vector == NULL_RTX) + { + zero_vector = reg; + tmp = gen_rtx_SET (reg, zero_rtx); + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_vector); + else if (mode == HImode) + if (zero_mask == NULL_RTX) + { + zero_mask = reg; + tmp = gen_rtx_SET (reg, zero_rtx); + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_mask); + else if (mode == V4HImode) + if (zero_mmx == NULL_RTX) + { + zero_mmx = reg; + tmp = gen_rtx_SET (reg, zero_rtx); + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_mmx); + else + gcc_unreachable (); + } + return zeroed_hardregs; +} + /* Define how to find the value returned by a function. VALTYPE is the data type of the value (as a tree). If the precise function being called is known, FUNC is its FUNCTION_DECL; @@ -23229,6 +23520,9 @@ ix86_run_selftests (void) #undef TARGET_FUNCTION_VALUE_REGNO_P #define TARGET_FUNCTION_VALUE_REGNO_P ix86_function_value_regno_p +#undef TARGET_ZERO_CALL_USED_REGS +#define TARGET_ZERO_CALL_USED_REGS ix86_zero_call_used_regs + #undef TARGET_PROMOTE_FUNCTION_MODE #define TARGET_PROMOTE_FUNCTION_MODE ix86_promote_function_mode diff --git a/gcc/df-scan.c b/gcc/df-scan.c index 93b060f..9e75c13 100644 --- a/gcc/df-scan.c +++ b/gcc/df-scan.c @@ -3614,6 +3614,14 @@ df_update_entry_block_defs (void) } +/* Return true if REGNO is used by the epilogue. */ +bool +df_epilogue_uses_p (unsigned int regno) +{ + return (EPILOGUE_USES (regno) + || TEST_HARD_REG_BIT (crtl->must_be_zero_on_return, regno)); +} + /* Set the bit for regs that are considered being used at the exit. */ static void @@ -3661,7 +3669,7 @@ df_get_exit_block_use_set (bitmap exit_block_uses) epilogue as being live at the end of the function since they may be referenced by our caller. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i] || EPILOGUE_USES (i)) + if (global_regs[i] || df_epilogue_uses_p (i)) bitmap_set_bit (exit_block_uses, i); if (targetm.have_epilogue () && epilogue_completed) @@ -3802,7 +3810,6 @@ df_hard_reg_init (void) initialized = true; } - /* Recompute the parts of scanning that are based on regs_ever_live because something changed in that array. */ @@ -3862,7 +3869,6 @@ df_regs_ever_live_p (unsigned int regno) return regs_ever_live[regno]; } - /* Set regs_ever_live[REGNO] to VALUE. If this cause regs_ever_live to change, schedule that change for the next update. */ diff --git a/gcc/df.h b/gcc/df.h index 8b6ca8c..0f098d7 100644 --- a/gcc/df.h +++ b/gcc/df.h @@ -1085,6 +1085,7 @@ extern void df_update_entry_exit_and_calls (void); extern bool df_hard_reg_used_p (unsigned int); extern unsigned int df_hard_reg_used_count (unsigned int); extern bool df_regs_ever_live_p (unsigned int); +extern bool df_epilogue_uses_p (unsigned int); extern void df_set_regs_ever_live (unsigned int, bool); extern void df_compute_regs_ever_live (bool); extern void df_scan_verify (void); diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c9f7299..25b3909 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3992,6 +3992,92 @@ performing a link with relocatable output (i.e.@: @code{ld -r}) on them. A declaration to which @code{weakref} is attached and that is associated with a named @code{target} must be @code{static}. +@item zero_call_used_regs ("@var{choice}") +@cindex @code{zero_call_used_regs} function attribute + +The @code{zero_call_used_regs} attribute causes the compiler to zero +a subset of all call-used registers@footnote{A ``call-used'' register +is a register whose contents can be changed by a function call; +therefore, a caller cannot assume that the register has the same contents +on return from the function as it had before calling the function. Such +registers are also called ``call-clobbered'', ``caller-saved'', or +``volatile''.} at function return. +This is used to increase program security by either mitigating +Return-Oriented Programming (ROP) attacks or preventing information leakage +through registers. + +In order to satisfy users with different security needs and control the +run-time overhead at the same time, @var{choice} parameter provides a +flexible way to choose the subset of the call-used registers to be zeroed. +The three basic values of @var{choice} are: + +@itemize @bullet +@item +@samp{skip} doesn't zero any call-used registers. + +@item +@samp{used} only zeros call-used registers that are used in the function. +A ``used'' register is one whose content has been set or referenced in +the function. + +@item +@samp{all} zeros all call-used registers. +@end itemize + +In addition to these three basic choices, it is possible to modify +@samp{used} or @samp{all} as follows: + +@itemize @bullet +@item +Adding @samp{-gpr} restricts the zeroing to general-purpose registers. + +@item +Adding @samp{-arg} restricts the zeroing to registers that can sometimes +be used to pass function arguments. This includes all argument registers +defined by the platform's calling conversion, regardless of whether the +function uses those registers for function arguments or not. +@end itemize + +The modifiers can be used individually or together. If they are used +together, they must appear in the order above. + +The full list of @var{choice}s is therefore: + +@table @code +@item skip +doesn't zero any call-used register. + +@item used +only zeros call-used registers that are used in the function. + +@item used-gpr +only zeros call-used general purpose registers that are used in the function. + +@item used-arg +only zeros call-used registers that are used in the function and pass arguments. + +@item used-gpr-arg +only zeros call-used general purpose registers that are used in the function +and pass arguments. + +@item all +zeros all call-used registers. + +@item all-gpr +zeros all call-used general purpose registers. + +@item all-arg +zeros all call-used registers that pass arguments. + +@item all-gpr-arg +zeros all call-used general purpose registers that pass +arguments. +@end table + +Of this list, @samp{used-arg}, @samp{used-gpr-arg}, @samp{all-arg}, +and @samp{all-gpr-arg} are mainly used for ROP mitigation. + +The default for the attribute is controlled by @option{-fzero-call-used-regs}. @end table @c This is the end of the target-independent attribute table diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index c049932..23834a2 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -550,7 +550,7 @@ Objective-C and Objective-C++ Dialects}. -funit-at-a-time -funroll-all-loops -funroll-loops @gol -funsafe-math-optimizations -funswitch-loops @gol -fipa-ra -fvariable-expansion-in-unroller -fvect-cost-model -fvpt @gol --fweb -fwhole-program -fwpa -fuse-linker-plugin @gol +-fweb -fwhole-program -fwpa -fuse-linker-plugin -fzero-call-used-regs @gol --param @var{name}=@var{value} -O -O0 -O1 -O2 -O3 -Os -Ofast -Og} @@ -12550,6 +12550,19 @@ int foo (void) Not all targets support this option. +@item -fzero-call-used-regs=@var{choice} +@opindex fzero-call-used-regs +Zero call-used registers at function return to increase program +security by either mitigating Return-Oriented Programming (ROP) +attacks or preventing information leakage through registers. + +The possible values of @var{choice} are the same as for the +@code{zero_call_used_regs} attribute (@pxref{Function Attributes}). +The default is @samp{skip}. + +You can control this behavior for a specific function by using the function +attribute @code{zero_call_used_regs} (@pxref{Function Attributes}). + @item --param @var{name}=@var{value} @opindex param In some places, GCC uses various constants to control the amount of diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 97437e8..18f8ad9 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -12053,6 +12053,18 @@ argument list due to stack realignment. Return @code{NULL} if no DRAP is needed. @end deftypefn +@deftypefn {Target Hook} HARD_REG_SET TARGET_ZERO_CALL_USED_REGS (HARD_REG_SET @var{selected_regs}) +This target hook emits instructions to zero the subset of @var{selected_regs} +that could conceivably contain values that are useful to an attacker. +Return the set of registers that were actually cleared. + +The default implementation uses normal move instructions to zero +all the registers in @var{selected_regs}. Define this hook if the +target has more efficient ways of zeroing certain registers, +or if you believe that certain registers would never contain +values that are useful to an attacker. +@end deftypefn + @deftypefn {Target Hook} bool TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS (void) When optimization is disabled, this hook indicates whether or not arguments should be allocated to stack slots. Normally, GCC allocates diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 412e22c..a67dbea 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -8111,6 +8111,8 @@ and the associated definitions of those functions. @hook TARGET_GET_DRAP_RTX +@hook TARGET_ZERO_CALL_USED_REGS + @hook TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS @hook TARGET_CONST_ANCHOR diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h index 92ad0dd6..4658c4a 100644 --- a/gcc/emit-rtl.h +++ b/gcc/emit-rtl.h @@ -310,6 +310,9 @@ struct GTY(()) rtl_data { sets them. */ HARD_REG_SET asm_clobbers; + /* All hard registers that need to be zeroed at the return of the routine. */ + HARD_REG_SET must_be_zero_on_return; + /* The highest address seen during shorten_branches. */ int max_insn_address; }; diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 852ea76..51c7e5f 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -20,6 +20,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_FLAG_TYPES_H #define GCC_FLAG_TYPES_H +#if !defined(IN_LIBGCC2) && !defined(IN_TARGET_LIBS) && !defined(IN_RTS) + enum debug_info_type { NO_DEBUG, /* Write no debug info. */ @@ -285,6 +287,24 @@ enum sanitize_code { | SANITIZE_BOUNDS_STRICT }; +/* Different settings for zeroing subset of registers. */ +namespace zero_regs_flag { + const unsigned int UNSET = 0; + const unsigned int SKIP = 1UL << 0; + const unsigned int ONLY_USED = 1UL << 1; + const unsigned int ONLY_GPR = 1UL << 2; + const unsigned int ONLY_ARG = 1UL << 3; + const unsigned int ENABLED = 1UL << 4; + const unsigned int USED_GPR_ARG = ENABLED | ONLY_USED | ONLY_GPR | ONLY_ARG; + const unsigned int USED_GPR = ENABLED | ONLY_USED | ONLY_GPR; + const unsigned int USED_ARG = ENABLED | ONLY_USED | ONLY_ARG; + const unsigned int USED = ENABLED | ONLY_USED; + const unsigned int ALL_GPR_ARG = ENABLED | ONLY_GPR | ONLY_ARG; + const unsigned int ALL_GPR = ENABLED | ONLY_GPR; + const unsigned int ALL_ARG = ENABLED | ONLY_ARG; + const unsigned int ALL = ENABLED; +} + /* Settings of flag_incremental_link. */ enum incremental_link { INCREMENTAL_LINK_NONE, @@ -382,4 +402,6 @@ enum parloops_schedule_type PARLOOPS_SCHEDULE_RUNTIME }; +#endif + #endif /* ! GCC_FLAG_TYPES_H */ diff --git a/gcc/function.c b/gcc/function.c index c612959..76a5037 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -46,10 +46,12 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "expmed.h" #include "optabs.h" +#include "opts.h" #include "regs.h" #include "emit-rtl.h" #include "recog.h" #include "rtl-error.h" +#include "hard-reg-set.h" #include "alias.h" #include "fold-const.h" #include "stor-layout.h" @@ -5815,6 +5817,103 @@ make_prologue_seq (void) return seq; } +/* Emit a sequence of insns to zero the call-used registers before RET + according to ZERO_REGS_TYPE. */ + +static void +gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type) +{ + bool only_gpr = true; + bool only_used = true; + bool only_arg = true; + + /* No need to zero call-used-regs in main (). */ + if (MAIN_NAME_P (DECL_NAME (current_function_decl))) + return; + + /* No need to zero call-used-regs if __builtin_eh_return is called + since it isn't a normal function return. */ + if (crtl->calls_eh_return) + return; + + /* If only_gpr is true, only zero call-used registers that are + general-purpose registers; if only_used is true, only zero + call-used registers that are used in the current function; + if only_arg is true, only zero call-used registers that pass + parameters defined by the flatform's calling conversion. */ + + using namespace zero_regs_flag; + + only_gpr = zero_regs_type & ONLY_GPR; + only_used = zero_regs_type & ONLY_USED; + only_arg = zero_regs_type & ONLY_ARG; + + /* For each of the hard registers, we should zero it if: + 1. it is a call-used register; + and 2. it is not a fixed register; + and 3. it is not live at the return of the routine; + and 4. it is general registor if only_gpr is true; + and 5. it is used in the routine if only_used is true; + and 6. it is a register that passes parameter if only_arg is true. */ + + /* First, prepare the data flow information. */ + basic_block bb = BLOCK_FOR_INSN (ret); + auto_bitmap live_out; + bitmap_copy (live_out, df_get_live_out (bb)); + df_simulate_initialize_backwards (bb, live_out); + df_simulate_one_insn_backwards (bb, ret, live_out); + + HARD_REG_SET selected_hardregs; + CLEAR_HARD_REG_SET (selected_hardregs); + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (!crtl->abi->clobbers_full_reg_p (regno)) + continue; + if (fixed_regs[regno]) + continue; + if (REGNO_REG_SET_P (live_out, regno)) + continue; + if (only_gpr + && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno)) + continue; + if (only_used && !df_regs_ever_live_p (regno)) + continue; + if (only_arg && !FUNCTION_ARG_REGNO_P (regno)) + continue; + + /* Now this is a register that we might want to zero. */ + SET_HARD_REG_BIT (selected_hardregs, regno); + } + + if (hard_reg_set_empty_p (selected_hardregs)) + return; + + /* Now that we have a hard register set that needs to be zeroed, pass it to + target to generate zeroing sequence. */ + HARD_REG_SET zeroed_hardregs; + start_sequence (); + zeroed_hardregs = targetm.calls.zero_call_used_regs (selected_hardregs); + rtx_insn *seq = get_insns (); + end_sequence (); + if (seq) + { + /* Emit the memory blockage and register clobber asm volatile before + the whole sequence. */ + start_sequence (); + expand_asm_reg_clobber_mem_blockage (zeroed_hardregs); + rtx_insn *seq_barrier = get_insns (); + end_sequence (); + + emit_insn_before (seq_barrier, ret); + emit_insn_before (seq, ret); + + /* Update the data flow information. */ + crtl->must_be_zero_on_return |= zeroed_hardregs; + df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun)); + } +} + + /* Return a sequence to be used as the epilogue for the current function, or NULL. */ @@ -6486,7 +6585,96 @@ make_pass_thread_prologue_and_epilogue (gcc::context *ctxt) { return new pass_thread_prologue_and_epilogue (ctxt); } - + +namespace { + +const pass_data pass_data_zero_call_used_regs = +{ + RTL_PASS, /* type */ + "zero_call_used_regs", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_zero_call_used_regs: public rtl_opt_pass +{ +public: + pass_zero_call_used_regs (gcc::context *ctxt) + : rtl_opt_pass (pass_data_zero_call_used_regs, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *); + +}; // class pass_zero_call_used_regs + +unsigned int +pass_zero_call_used_regs::execute (function *fun) +{ + using namespace zero_regs_flag; + unsigned int zero_regs_type = UNSET; + + tree attr_zero_regs = lookup_attribute ("zero_call_used_regs", + DECL_ATTRIBUTES (fun->decl)); + + /* Get the type of zero_call_used_regs from function attribute. + We have filtered out invalid attribute values already at this point. */ + if (attr_zero_regs) + { + /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE + is the attribute argument's value. */ + attr_zero_regs = TREE_VALUE (attr_zero_regs); + gcc_assert (TREE_CODE (attr_zero_regs) == TREE_LIST); + attr_zero_regs = TREE_VALUE (attr_zero_regs); + gcc_assert (TREE_CODE (attr_zero_regs) == STRING_CST); + + for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i) + if (strcmp (TREE_STRING_POINTER (attr_zero_regs), + zero_call_used_regs_opts[i].name) == 0) + { + zero_regs_type = zero_call_used_regs_opts[i].flag; + break; + } + } + + if (!zero_regs_type) + zero_regs_type = flag_zero_call_used_regs; + + /* No need to zero call-used-regs when no user request is present. */ + if (!(zero_regs_type & ENABLED)) + return 0; + + edge_iterator ei; + edge e; + + /* This pass needs data flow information. */ + df_analyze (); + + /* Iterate over the function's return instructions and insert any + register zeroing required by the -fzero-call-used-regs command-line + option or the "zero_call_used_regs" function attribute. */ + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + { + rtx_insn *insn = BB_END (e->src); + if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn))) + gen_call_used_regs_seq (insn, zero_regs_type); + } + + return 0; +} + +} // anon namespace + +rtl_opt_pass * +make_pass_zero_call_used_regs (gcc::context *ctxt) +{ + return new pass_zero_call_used_regs (ctxt); +} /* If CONSTRAINT is a matching constraint, then return its number. Otherwise, return -1. */ diff --git a/gcc/optabs.c b/gcc/optabs.c index 8ad7f4b..1820b91 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -6484,6 +6484,48 @@ expand_memory_blockage (void) expand_asm_memory_blockage (); } +/* Generate asm volatile("" : : : "memory") as a memory blockage, at the + same time clobbering the register set specified by REGS. */ + +void +expand_asm_reg_clobber_mem_blockage (HARD_REG_SET regs) +{ + rtx asm_op, clob_mem; + + unsigned int num_of_regs = 0; + for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (TEST_HARD_REG_BIT (regs, i)) + num_of_regs++; + + asm_op = gen_rtx_ASM_OPERANDS (VOIDmode, "", "", 0, + rtvec_alloc (0), rtvec_alloc (0), + rtvec_alloc (0), UNKNOWN_LOCATION); + MEM_VOLATILE_P (asm_op) = 1; + + rtvec v = rtvec_alloc (num_of_regs + 2); + + clob_mem = gen_rtx_SCRATCH (VOIDmode); + clob_mem = gen_rtx_MEM (BLKmode, clob_mem); + clob_mem = gen_rtx_CLOBBER (VOIDmode, clob_mem); + + RTVEC_ELT (v, 0) = asm_op; + RTVEC_ELT (v, 1) = clob_mem; + + if (num_of_regs > 0) + { + unsigned int j = 2; + for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (TEST_HARD_REG_BIT (regs, i)) + { + RTVEC_ELT (v, j) = gen_rtx_CLOBBER (VOIDmode, regno_reg_rtx[i]); + j++; + } + gcc_assert (j == (num_of_regs + 2)); + } + + emit_insn (gen_rtx_PARALLEL (VOIDmode, v)); +} + /* This routine will either emit the mem_thread_fence pattern or issue a sync_synchronize to generate a fence for memory model MEMMODEL. */ diff --git a/gcc/optabs.h b/gcc/optabs.h index 0b14700..bfa10c8 100644 --- a/gcc/optabs.h +++ b/gcc/optabs.h @@ -345,6 +345,8 @@ rtx expand_atomic_store (rtx, rtx, enum memmodel, bool); rtx expand_atomic_fetch_op (rtx, rtx, rtx, enum rtx_code, enum memmodel, bool); +extern void expand_asm_reg_clobber_mem_blockage (HARD_REG_SET); + extern bool insn_operand_matches (enum insn_code icode, unsigned int opno, rtx operand); extern bool valid_multiword_target_p (rtx); diff --git a/gcc/opts.c b/gcc/opts.c index 3bda59a..8c439e4 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1776,6 +1776,25 @@ const struct sanitizer_opts_s coverage_sanitizer_opts[] = { NULL, 0U, 0UL, false } }; +using namespace zero_regs_flag; +/* -fzero-call-used-regs= suboptions. */ +const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] = +{ +#define ZERO_CALL_USED_REGS_OPT(name, flags) \ + { #name, flags } + ZERO_CALL_USED_REGS_OPT (skip, SKIP), + ZERO_CALL_USED_REGS_OPT (used-gpr-arg, USED_GPR_ARG), + ZERO_CALL_USED_REGS_OPT (used-gpr, USED_GPR), + ZERO_CALL_USED_REGS_OPT (used-arg, USED_ARG), + ZERO_CALL_USED_REGS_OPT (used, USED), + ZERO_CALL_USED_REGS_OPT (all-gpr-arg, ALL_GPR_ARG), + ZERO_CALL_USED_REGS_OPT (all-gpr, ALL_GPR), + ZERO_CALL_USED_REGS_OPT (all-arg, ALL_ARG), + ZERO_CALL_USED_REGS_OPT (all, ALL), +#undef ZERO_CALL_USED_REGS_OPT + {NULL, 0U} +}; + /* A struct for describing a run of chars within a string. */ class string_fragment @@ -1970,6 +1989,27 @@ parse_no_sanitize_attribute (char *value) return flags; } +/* Parse -fzero-call-used-regs suboptions from ARG, return the FLAGS. */ + +unsigned int +parse_zero_call_used_regs_options (const char *arg) +{ + unsigned int flags = 0; + + /* Check to see if the string matches a sub-option name. */ + for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i) + if (strcmp (arg, zero_call_used_regs_opts[i].name) == 0) + { + flags = zero_call_used_regs_opts[i].flag; + break; + } + + if (!flags) + error ("unrecognized argument to %<-fzero-call-used-regs=%>: %qs", arg); + + return flags; +} + /* Parse -falign-NAME format for a FLAG value. Return individual parsed integer values into RESULT_VALUES array. If REPORT_ERROR is set, print error message at LOC location. */ @@ -2601,6 +2641,11 @@ common_handle_option (struct gcc_options *opts, /* Automatically sets -ftree-loop-vectorize and -ftree-slp-vectorize. Nothing more to do here. */ break; + case OPT_fzero_call_used_regs_: + opts->x_flag_zero_call_used_regs + = parse_zero_call_used_regs_options (arg); + break; + case OPT_fshow_column: dc->show_column = value; break; diff --git a/gcc/opts.h b/gcc/opts.h index 8f594b4..7d1e126 100644 --- a/gcc/opts.h +++ b/gcc/opts.h @@ -444,6 +444,12 @@ extern const struct sanitizer_opts_s bool can_recover; } sanitizer_opts[]; +extern const struct zero_call_used_regs_opts_s +{ + const char *const name; + unsigned int flag; +} zero_call_used_regs_opts[]; + extern vec<const char *> help_option_arguments; extern void add_misspelling_candidates (auto_vec<char *> *candidates, diff --git a/gcc/passes.def b/gcc/passes.def index f865bdc..77d4676 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -492,6 +492,7 @@ along with GCC; see the file COPYING3. If not see POP_INSERT_PASSES () NEXT_PASS (pass_late_compilation); PUSH_INSERT_PASSES_WITHIN (pass_late_compilation) + NEXT_PASS (pass_zero_call_used_regs); NEXT_PASS (pass_compute_alignments); NEXT_PASS (pass_variable_tracking); NEXT_PASS (pass_free_cfg); diff --git a/gcc/recog.c b/gcc/recog.c index ce83b7f..b458746 100644 --- a/gcc/recog.c +++ b/gcc/recog.c @@ -922,7 +922,23 @@ validate_simplify_insn (rtx_insn *insn) } return ((num_changes_pending () > 0) && (apply_change_group () > 0)); } - + +/* Check whether INSN matches a specific alternative of an .md pattern. */ + +bool +valid_insn_p (rtx_insn *insn) +{ + recog_memoized (insn); + if (INSN_CODE (insn) < 0) + return false; + extract_insn (insn); + /* We don't know whether the insn will be in code that is optimized + for size or speed, so consider all enabled alternatives. */ + if (!constrain_operands (1, get_enabled_alternatives (insn))) + return false; + return true; +} + /* Return 1 if OP is a valid general operand for machine mode MODE. This is either a register reference, a memory reference, or a constant. In the case of a memory reference, the address diff --git a/gcc/recog.h b/gcc/recog.h index ae3675f..d87456c 100644 --- a/gcc/recog.h +++ b/gcc/recog.h @@ -113,6 +113,7 @@ extern void validate_replace_src_group (rtx, rtx, rtx_insn *); extern bool validate_simplify_insn (rtx_insn *insn); extern int num_changes_pending (void); extern bool reg_fits_class_p (const_rtx, reg_class_t, int, machine_mode); +extern bool valid_insn_p (rtx_insn *); extern int offsettable_memref_p (rtx); extern int offsettable_nonstrict_memref_p (rtx); diff --git a/gcc/resource.c b/gcc/resource.c index 0a9d594..90cf091 100644 --- a/gcc/resource.c +++ b/gcc/resource.c @@ -1186,7 +1186,7 @@ init_resource_info (rtx_insn *epilogue_insn) &end_of_function_needs, true); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i] || EPILOGUE_USES (i)) + if (global_regs[i] || df_epilogue_uses_p (i)) SET_HARD_REG_BIT (end_of_function_needs.regs, i); /* The registers required to be live at the end of the function are diff --git a/gcc/target.def b/gcc/target.def index ed2da15..b916635 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -5080,6 +5080,21 @@ argument list due to stack realignment. Return @code{NULL} if no DRAP\n\ is needed.", rtx, (void), NULL) +/* Generate instruction sequence to zero call used registers. */ +DEFHOOK +(zero_call_used_regs, + "This target hook emits instructions to zero the subset of @var{selected_regs}\n\ +that could conceivably contain values that are useful to an attacker.\n\ +Return the set of registers that were actually cleared.\n\ +\n\ +The default implementation uses normal move instructions to zero\n\ +all the registers in @var{selected_regs}. Define this hook if the\n\ +target has more efficient ways of zeroing certain registers,\n\ +or if you believe that certain registers would never contain\n\ +values that are useful to an attacker.", + HARD_REG_SET, (HARD_REG_SET selected_regs), +default_zero_call_used_regs) + /* Return true if all function parameters should be spilled to the stack. */ DEFHOOK diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 5d94fce..4e4d100 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -56,6 +56,9 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-alias.h" #include "gimple-expr.h" #include "memmodel.h" +#include "backend.h" +#include "emit-rtl.h" +#include "df.h" #include "tm_p.h" #include "stringpool.h" #include "tree-vrp.h" @@ -987,6 +990,35 @@ default_function_value_regno_p (const unsigned int regno ATTRIBUTE_UNUSED) #endif } +/* The default hook for TARGET_ZERO_CALL_USED_REGS. */ + +HARD_REG_SET +default_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs) +{ + gcc_assert (!hard_reg_set_empty_p (need_zeroed_hardregs)); + + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + { + rtx_insn *last_insn = get_last_insn (); + machine_mode mode = GET_MODE (regno_reg_rtx[regno]); + rtx zero = CONST0_RTX (mode); + rtx_insn *insn = emit_move_insn (regno_reg_rtx[regno], zero); + if (!valid_insn_p (insn)) + { + static bool issued_error; + if (!issued_error) + { + issued_error = true; + sorry ("%qs not supported on this target", + "-fzero-call-used_regs"); + } + delete_insns_since (last_insn); + } + } + return need_zeroed_hardregs; +} + rtx default_internal_arg_pointer (void) { diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 44ab926..e0a925f 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -160,6 +160,7 @@ extern unsigned int default_function_arg_round_boundary (machine_mode, const_tree); extern bool hook_bool_const_rtx_commutative_p (const_rtx, int); extern rtx default_function_value (const_tree, const_tree, bool); +extern HARD_REG_SET default_zero_call_used_regs (HARD_REG_SET); extern rtx default_libcall_value (machine_mode, const_rtx); extern bool default_function_value_regno_p (const unsigned int); extern rtx default_internal_arg_pointer (void); diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c new file mode 100644 index 0000000..2463353 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c new file mode 100644 index 0000000..bdaf8e7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c @@ -0,0 +1,92 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#include <assert.h> +int result = 0; + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("skip"))) +foo1 (int x) +{ + return (x + 1); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-gpr-arg"))) +foo2 (int x) +{ + return (x + 2); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-gpr"))) +foo3 (int x) +{ + return (x + 3); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-arg"))) +foo4 (int x) +{ + return (x + 4); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used"))) +foo5 (int x) +{ + return (x + 5); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-gpr-arg"))) +foo6 (int x) +{ + return (x + 6); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-gpr"))) +foo7 (int x) +{ + return (x + 7); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-arg"))) +foo8 (int x) +{ + return (x + 8); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all"))) +foo9 (int x) +{ + return (x + 9); +} + +int main() +{ + result = foo1 (1); + result += foo2 (1); + result += foo3 (1); + result += foo4 (1); + result += foo5 (1); + result += foo6 (1); + result += foo7 (1); + result += foo8 (1); + result += foo9 (1); + assert (result == 54); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c new file mode 100644 index 0000000..7721e39 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +#include "zero-scratch-regs-10.c" 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..25891ac --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */ + +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c new file mode 100644 index 0000000..7c3d286 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ + +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c new file mode 100644 index 0000000..ba28c06 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */ + +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c new file mode 100644 index 0000000..26679a4 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ + +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c new file mode 100644 index 0000000..80f5bbb --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */ + +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c new file mode 100644 index 0000000..159f35c --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c new file mode 100644 index 0000000..c1faaf0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */ + +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c new file mode 100644 index 0000000..3f14bac --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c @@ -0,0 +1,4 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +#include "zero-scratch-regs-1.c" diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c new file mode 100644 index 0000000..1e75795 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +int result __attribute__ ((zero_call_used_regs("all"))); /* { dg-error "attribute applies only to functions" } */ +int +__attribute__ ((zero_call_used_regs("gpr-arg-all"))) +foo1 (int x) /* { dg-error "unrecognized 'zero_call_used_regs' attribute argument" } */ +{ + return (x + 1); +} +int +__attribute__ ((zero_call_used_regs(1))) +foo2 (int x) /* { dg-error "argument not a string" } */ +{ + return (x + 2); +} diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c new file mode 100644 index 0000000..9f61dc4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c new file mode 100644 index 0000000..09048e5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +extern int foo (int) __attribute__ ((zero_call_used_regs("all-gpr"))); + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c new file mode 100644 index 0000000..4862688 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c @@ -0,0 +1,39 @@ +/* { dg-do run { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ + +struct S { int i; }; +__attribute__((const, noinline, noclone)) +struct S foo (int x) +{ + struct S s; + s.i = x; + return s; +} + +int a[2048], b[2048], c[2048], d[2048]; +struct S e[2048]; + +__attribute__((noinline, noclone)) void +bar (void) +{ + int i; + for (i = 0; i < 1024; i++) + { + e[i] = foo (i); + a[i+2] = a[i] + a[i+1]; + b[10] = b[10] + i; + c[i] = c[2047 - i]; + d[i] = d[i + 1]; + } +} + +int +main () +{ + int i; + bar (); + for (i = 0; i < 1024; i++) + if (e[i].i != i) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c new file mode 100644 index 0000000..500251b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c @@ -0,0 +1,39 @@ +/* { dg-do run { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +struct S { int i; }; +__attribute__((const, noinline, noclone)) +struct S foo (int x) +{ + struct S s; + s.i = x; + return s; +} + +int a[2048], b[2048], c[2048], d[2048]; +struct S e[2048]; + +__attribute__((noinline, noclone)) void +bar (void) +{ + int i; + for (i = 0; i < 1024; i++) + { + e[i] = foo (i); + a[i+2] = a[i] + a[i+1]; + b[10] = b[10] + i; + c[i] = c[2047 - i]; + d[i] = d[i + 1]; + } +} + +int +main () +{ + int i; + bar (); + for (i = 0; i < 1024; i++) + if (e[i].i != i) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c new file mode 100644 index 0000000..8b058e3 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { target { ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 15 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c new file mode 100644 index 0000000..d4eaaf7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-times "vzeroall" 1 } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c new file mode 100644 index 0000000..dd3bb90 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +extern void foo (void) __attribute__ ((zero_call_used_regs("used"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c new file mode 100644 index 0000000..e2274f6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +extern void foo (void) __attribute__ ((zero_call_used_regs("skip"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c new file mode 100644 index 0000000..7f5d153 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c new file mode 100644 index 0000000..fe13d2b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c new file mode 100644 index 0000000..205a532 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm2, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c new file mode 100644 index 0000000..e046684 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c new file mode 100644 index 0000000..4be8ff6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c @@ -0,0 +1,23 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" { target { ia32 } } } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { target { ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm1, %xmm\[0-9\]+" 14 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c new file mode 100644 index 0000000..0eb34e0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip -march=corei7" } */ + +__attribute__ ((zero_call_used_regs("used"))) +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c new file mode 100644 index 0000000..0258c70 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler "vzeroall" } } */ +/* { dg-final { scan-assembler-times "fldz" 8 } } */ +/* { dg-final { scan-assembler-times "fstp" 8 } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c new file mode 100644 index 0000000..0625eb5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c @@ -0,0 +1,29 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx512f" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler "vzeroall" } } */ +/* { dg-final { scan-assembler-times "fldz" 8 } } */ +/* { dg-final { scan-assembler-times "fstp" 8 } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kxorw\[ \t\]*%k0, %k0, %k0" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k3" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k4" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k5" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k6" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k7" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c new file mode 100644 index 0000000..208633e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c @@ -0,0 +1,10 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c new file mode 100644 index 0000000..21e82c6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c @@ -0,0 +1,10 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c new file mode 100644 index 0000000..293d2fe --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c @@ -0,0 +1,23 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm1" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm2" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm3" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm4" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm5" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm6" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm7" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c new file mode 100644 index 0000000..c34e6af --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c @@ -0,0 +1,15 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c new file mode 100644 index 0000000..48b1f01 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c @@ -0,0 +1,16 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mmmx -fzero-call-used-regs=all" } */ +/* { dg-require-effective-target ia32 } */ + +__v2si ret_mmx (void) +{ + return (__v2si) { 123, 345 }; +} + +/* { dg-final { scan-assembler "pxor\[ \t\]*%mm1, %mm1" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm2" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm3" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm4" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm5" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm6" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm7" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c new file mode 100644 index 0000000..8b5e1cd --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c @@ -0,0 +1,10 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +long double ret_x87 (void) +{ + return 1.1L; +} + +/* { dg-final { scan-assembler-times "fldz" 7 } } */ +/* { dg-final { scan-assembler-times "fstp" 7 } } */ 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-30.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c new file mode 100644 index 0000000..2a6f38f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +_Complex long double ret_x87_cplx (void) +{ + return 1.1L + 1.2iL; +} + +/* { dg-final { scan-assembler-times "fldz" 8 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "fstp" 8 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "fldz" 6 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "fstp" 6 { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c new file mode 100644 index 0000000..df71286 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mmmx -fzero-call-used-regs=all-arg" } */ +/* { dg-require-effective-target ia32 } */ + +__v2si ret_mmx (void) +{ + return (__v2si) { 123, 345 }; +} + +/* { dg-final { scan-assembler "pxor\[ \t\]*%mm1, %mm1" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm2" } } */ +/* { dg-final { scan-assembler-not "movq\[ \t\]*%mm1, %mm\[34567\]" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c new file mode 100644 index 0000000..ccfa441 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +extern void foo (void) __attribute__ ((zero_call_used_regs("used-gpr"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c new file mode 100644 index 0000000..6b46ca3 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c @@ -0,0 +1,20 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +__attribute__ ((zero_call_used_regs("all-gpr"))) +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c new file mode 100644 index 0000000..0680f38 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +extern void foo (void) __attribute__ ((zero_call_used_regs("skip"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c new file mode 100644 index 0000000..534defa --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c new file mode 100644 index 0000000..477bb19 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c new file mode 100644 index 0000000..a305a60 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c @@ -0,0 +1,15 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +extern int foo (int) __attribute__ ((zero_call_used_regs("used-gpr"))); + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 62e5b69..8afe8ee 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -592,6 +592,7 @@ extern rtl_opt_pass *make_pass_gcse2 (gcc::context *ctxt); extern rtl_opt_pass *make_pass_split_after_reload (gcc::context *ctxt); extern rtl_opt_pass *make_pass_thread_prologue_and_epilogue (gcc::context *ctxt); +extern rtl_opt_pass *make_pass_zero_call_used_regs (gcc::context *ctxt); extern rtl_opt_pass *make_pass_stack_adjustments (gcc::context *ctxt); extern rtl_opt_pass *make_pass_sched_fusion (gcc::context *ctxt); extern rtl_opt_pass *make_pass_peephole2 (gcc::context *ctxt); -- 1.8.3.1