[zero-call-used-regs] Add leafy mode for zero-call-used-regs

Message ID orr0z1ljk2.fsf@lxoliva.fsfla.org
State New
Headers
Series [zero-call-used-regs] Add leafy mode for zero-call-used-regs |

Commit Message

Alexandre Oliva Oct. 21, 2022, 7:31 a.m. UTC
  Introduce 'leafy' to auto-select between 'used' and 'all' for leaf and
nonleaf functions, respectively.

Regstrapped on x86_64-linux-gnu.  Ok to install?


for  gcc/ChangeLog

	* doc/extend.texi (zero-call-used-regs): Document leafy and
	variants thereof.
	* flag-types.h (zero_regs_flags): Add LEAFY_MODE, as well as
	LEAFY and variants.
	* function.cc (gen_call_ued_regs_seq): Set only_used for leaf
	functions in leafy mode.
	* opts.cc (zero_call_used_regs_opts): Add leafy and variants.

for  gcc/testsuite/ChangeLog

	* c-c++-common/zero-scratch-regs-leafy-1.c: New.
	* c-c++-common/zero-scratch-regs-leafy-2.c: New.
	* gcc.target/i386/zero-scratch-regs-leafy-1.c: New.
	* gcc.target/i386/zero-scratch-regs-leafy-2.c: New.
---
 gcc/doc/extend.texi                                |   22 ++++++++++++++++++--
 gcc/flag-types.h                                   |    5 +++++
 gcc/function.cc                                    |    3 +++
 gcc/opts.cc                                        |    4 ++++
 .../c-c++-common/zero-scratch-regs-leafy-1.c       |   15 ++++++++++++++
 .../c-c++-common/zero-scratch-regs-leafy-2.c       |   21 +++++++++++++++++++
 .../gcc.target/i386/zero-scratch-regs-leafy-1.c    |   12 +++++++++++
 .../gcc.target/i386/zero-scratch-regs-leafy-2.c    |   16 +++++++++++++++
 8 files changed, 96 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
  

Comments

Qing Zhao Oct. 21, 2022, 2:26 p.m. UTC | #1
Hi, Alexandre,

Could you please explain a little bit on the motivation of this patch first?

thanks.

Qing

> On Oct 21, 2022, at 3:31 AM, Alexandre Oliva <oliva@adacore.com> wrote:
> 
> Introduce 'leafy' to auto-select between 'used' and 'all' for leaf and
> nonleaf functions, respectively.
> 
> Regstrapped on x86_64-linux-gnu.  Ok to install?
> 
> 
> for  gcc/ChangeLog
> 
> 	* doc/extend.texi (zero-call-used-regs): Document leafy and
> 	variants thereof.
> 	* flag-types.h (zero_regs_flags): Add LEAFY_MODE, as well as
> 	LEAFY and variants.
> 	* function.cc (gen_call_ued_regs_seq): Set only_used for leaf
> 	functions in leafy mode.
> 	* opts.cc (zero_call_used_regs_opts): Add leafy and variants.
> 
> for  gcc/testsuite/ChangeLog
> 
> 	* c-c++-common/zero-scratch-regs-leafy-1.c: New.
> 	* c-c++-common/zero-scratch-regs-leafy-2.c: New.
> 	* gcc.target/i386/zero-scratch-regs-leafy-1.c: New.
> 	* gcc.target/i386/zero-scratch-regs-leafy-2.c: New.
> ---
> gcc/doc/extend.texi                                |   22 ++++++++++++++++++--
> gcc/flag-types.h                                   |    5 +++++
> gcc/function.cc                                    |    3 +++
> gcc/opts.cc                                        |    4 ++++
> .../c-c++-common/zero-scratch-regs-leafy-1.c       |   15 ++++++++++++++
> .../c-c++-common/zero-scratch-regs-leafy-2.c       |   21 +++++++++++++++++++
> .../gcc.target/i386/zero-scratch-regs-leafy-1.c    |   12 +++++++++++
> .../gcc.target/i386/zero-scratch-regs-leafy-2.c    |   16 +++++++++++++++
> 8 files changed, 96 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
> create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
> 
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 04af0584d82cc..bf11956c467fb 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -4391,10 +4391,28 @@ zeros all call-used registers that pass arguments.
> @item all-gpr-arg
> zeros all call-used general purpose registers that pass
> arguments.
> +
> +@item leafy
> +Same as @samp{used} in a leaf function, and same as @samp{all} in a
> +nonleaf function.
> +
> +@item leafy-gpr
> +Same as @samp{used-gpr} in a leaf function, and same as @samp{all-gpr}
> +in a nonleaf function.
> +
> +@item leafy-arg
> +Same as @samp{used-arg} in a leaf function, and same as @samp{all-arg}
> +in a nonleaf function.
> +
> +@item leafy-gpr-arg
> +Same as @samp{used-gpr-arg} in a leaf function, and same as
> +@samp{all-gpr-arg} in a nonleaf function.
> +
> @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.
> +Of this list, @samp{used-arg}, @samp{used-gpr-arg}, @samp{leafy-arg},
> +@samp{leafy-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
> diff --git a/gcc/flag-types.h b/gcc/flag-types.h
> index d2e751060ffce..b90c85167dcd4 100644
> --- a/gcc/flag-types.h
> +++ b/gcc/flag-types.h
> @@ -338,6 +338,7 @@ namespace zero_regs_flags {
>   const unsigned int ONLY_GPR = 1UL << 2;
>   const unsigned int ONLY_ARG = 1UL << 3;
>   const unsigned int ENABLED = 1UL << 4;
> +  const unsigned int LEAFY_MODE = 1UL << 5;
>   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;
> @@ -346,6 +347,10 @@ namespace zero_regs_flags {
>   const unsigned int ALL_GPR = ENABLED | ONLY_GPR;
>   const unsigned int ALL_ARG = ENABLED | ONLY_ARG;
>   const unsigned int ALL = ENABLED;
> +  const unsigned int LEAFY_GPR_ARG = ENABLED | LEAFY_MODE | ONLY_GPR | ONLY_ARG;
> +  const unsigned int LEAFY_GPR = ENABLED | LEAFY_MODE | ONLY_GPR;
> +  const unsigned int LEAFY_ARG = ENABLED | LEAFY_MODE | ONLY_ARG;
> +  const unsigned int LEAFY = ENABLED | LEAFY_MODE;
> }
> 
> /* Settings of flag_incremental_link.  */
> diff --git a/gcc/function.cc b/gcc/function.cc
> index 6474a663b30b8..16582e698041a 100644
> --- a/gcc/function.cc
> +++ b/gcc/function.cc
> @@ -5879,6 +5879,9 @@ gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type)
>   only_used = zero_regs_type & ONLY_USED;
>   only_arg = zero_regs_type & ONLY_ARG;
> 
> +  if ((zero_regs_type & LEAFY_MODE) && leaf_function_p ())
> +    only_used = true;
> +
>   /* 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;
> diff --git a/gcc/opts.cc b/gcc/opts.cc
> index ae079fcd20eea..39f6a1b278dc6 100644
> --- a/gcc/opts.cc
> +++ b/gcc/opts.cc
> @@ -2099,6 +2099,10 @@ const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
>   ZERO_CALL_USED_REGS_OPT (all-gpr, zero_regs_flags::ALL_GPR),
>   ZERO_CALL_USED_REGS_OPT (all-arg, zero_regs_flags::ALL_ARG),
>   ZERO_CALL_USED_REGS_OPT (all, zero_regs_flags::ALL),
> +  ZERO_CALL_USED_REGS_OPT (leafy-gpr-arg, zero_regs_flags::LEAFY_GPR_ARG),
> +  ZERO_CALL_USED_REGS_OPT (leafy-gpr, zero_regs_flags::LEAFY_GPR),
> +  ZERO_CALL_USED_REGS_OPT (leafy-arg, zero_regs_flags::LEAFY_ARG),
> +  ZERO_CALL_USED_REGS_OPT (leafy, zero_regs_flags::LEAFY),
> #undef ZERO_CALL_USED_REGS_OPT
>   {NULL, 0U}
> };
> diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
> new file mode 100644
> index 0000000000000..c1a0c31ba1c37
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
> @@ -0,0 +1,15 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fzero-call-used-regs=leafy" } */
> +
> +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-leafy-2.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
> new file mode 100644
> index 0000000000000..d450620c1fcfe
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
> @@ -0,0 +1,21 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +
> +#include <assert.h>
> +int result = 0;
> +
> +int 
> +__attribute__((noipa))
> +__attribute__ ((zero_call_used_regs("leafy")))
> +foo1 (int x)
> +{
> +  return (x + 1);
> +}
> +
> +int 
> +__attribute__((noipa))
> +__attribute__ ((zero_call_used_regs("leafy")))
> +foo2 (int x)
> +{
> +  return foo1 (x + 2);
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
> new file mode 100644
> index 0000000000000..2277710c771b7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
> @@ -0,0 +1,12 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fzero-call-used-regs=leafy -fno-stack-protector -fno-PIC" } */
> +
> +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-leafy-2.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
> new file mode 100644
> index 0000000000000..24b85c3dbb766
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
> @@ -0,0 +1,16 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fzero-call-used-regs=leafy-gpr -fno-stack-protector -fno-PIC" } */
> +
> +extern int bar (int);
> +
> +void
> +foo (void)
> +{
> +  int x = bar (0);
> +  if (x)
> +    bar (1);
> +}
> +
> +/* { dg-final { scan-assembler "xorl\[ \t\]+%eax, %eax" } } */
> +/* { dg-final { scan-assembler "xorl\[ \t\]+%edx, %edx" } } */
> +/* { dg-final { scan-assembler "xorl\[ \t\]+%ecx, %ecx" } } */
> 
> -- 
> Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
>   Free Software Activist                       GNU Toolchain Engineer
> Disinformation flourishes because many people care deeply about injustice
> but very few check the facts.  Ask me about <https://stallmansupport.org>
  
Alexandre Oliva Oct. 25, 2022, 2:48 a.m. UTC | #2
Hello, Qing,

It was a pleasure to meet you at the Cauldron.


On Oct 21, 2022, Qing Zhao <qing.zhao@oracle.com> wrote:

> Hi, Alexandre,
> Could you please explain a little bit on the motivation of this patch first?

It was a suggestion I got after the Cauldron presentation.
It made sense to me, and was easy enough to implement.

'all' for leaf functions is likely wasteful.  If no other functions are
called, one can determine exactly which registers might carry
information out and thus need zeroing, and 'used' is thus likely enough,
depending on the purpose of register scrubbing.  (In some scenarios, it
might make sense to want scrubbing of all registers, even unused ones
that carry incoming values)

Though some functions are coded as leaf functions, others may become
leaf functions because of inlining or other optimizations.  It's hard
for users to predict, so it makes sense to have a mode that tells the
compiler to figure it out.


There's room for a follow-up improvement, to save on a little more
potentially-wasteful anti-leaking scrubbing even in non-leaf functions:
for this purpose, they need not scrub registers that they don't use
themselves, if all potential callees are known to have scrubbed them.

I have not (yet?) implemented this variant; I haven't even found a name
I'm happy with for it.  (seal?  plug?  cork?  another leak antonym?)

I'm not entirely happy with leafy either, FWIW.  Bikeshedding anyone? :-)

https://gcc.gnu.org/pipermail/gcc-patches/2022-October/604083.html
  
Qing Zhao Oct. 25, 2022, 3:22 p.m. UTC | #3
Hi, Alexandre,

> On Oct 24, 2022, at 10:48 PM, Alexandre Oliva <oliva@adacore.com> wrote:
> 
> Hello, Qing,
> 
> It was a pleasure to meet you at the Cauldron.

Me too!
> 
> 
> On Oct 21, 2022, Qing Zhao <qing.zhao@oracle.com> wrote:
> 
>> Hi, Alexandre,
>> Could you please explain a little bit on the motivation of this patch first?
> 
> It was a suggestion I got after the Cauldron presentation.
> It made sense to me, and was easy enough to implement.
> 
> 'all' for leaf functions is likely wasteful.  If no other functions are
> called, one can determine exactly which registers might carry
> information out and thus need zeroing, and 'used' is thus likely enough,
> depending on the purpose of register scrubbing.  (In some scenarios, it
> might make sense to want scrubbing of all registers, even unused ones
> that carry incoming values)

Under what kinds of situations, we should clear the un-used registers for leaf functions?


> 
> Though some functions are coded as leaf functions, others may become
> leaf functions because of inlining or other optimizations.  It's hard
> for users to predict, so it makes sense to have a mode that tells the
> compiler to figure it out.

Yes, make sense.

Now I am wondering whether we should make “leafy” mode by default then?


Another thing is, do you have any information on how much this new mode can save the 
code size and run-time compared to mode “all”?


> 
> 
> There's room for a follow-up improvement, to save on a little more
> potentially-wasteful anti-leaking scrubbing even in non-leaf functions:
> for this purpose, they need not scrub registers that they don't use
> themselves, if all potential callees are known to have scrubbed them.

Yes, looks like another potential improvement we might add. 
> 
> I have not (yet?) implemented this variant; I haven't even found a name
> I'm happy with for it.  (seal?  plug?  cork?  another leak antonym?)

For this improvement, I am still thinking no need to add a new mode, just add it by default?

Qing
> 
> I'm not entirely happy with leafy either, FWIW.  Bikeshedding anyone? :-)
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2022-October/604083.html
> 
> -- 
> Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
>   Free Software Activist                       GNU Toolchain Engineer
> Disinformation flourishes because many people care deeply about injustice
> but very few check the facts.  Ask me about <https://stallmansupport.org>
  
Alexandre Oliva Oct. 26, 2022, 9:29 p.m. UTC | #4
On Oct 25, 2022, Qing Zhao <qing.zhao@oracle.com> wrote:

>> 'all' for leaf functions is likely wasteful.  If no other functions are
>> called, one can determine exactly which registers might carry
>> information out and thus need zeroing, and 'used' is thus likely enough,
>> depending on the purpose of register scrubbing.  (In some scenarios, it
>> might make sense to want scrubbing of all registers, even unused ones
>> that carry incoming values)

> Under what kinds of situations, we should clear the un-used registers
> for leaf functions?

The one (admittedly contrived) scenario that comes to mind is calling an
out-of-line, empty leaf function with 'all' for explicit register
scrubbing at desired points in the program.  I.e., this empty leaf
function would be in charge of scrubbing the caller's registers.  It
could even be tail-called.

I'm sure there are other scenarios in which keeping at least the
possibility of 'all' is useful.

> Now I am wondering whether we should make “leafy” mode by default then?

I'm not sure what you mean by default.  I think "skip" is the right
default for general use, where register scrubbing is not explicitly
requested.  When it is, perhaps -fzero-call-used-regs without
'=<choice>' could be 'leafy' indeed or, even better, the extended form
thereof that is in search of a name and so far unimplemented.

> Another thing is, do you have any information on how much this new mode can save the 
> code size and run-time compared to mode “all”?

I'm afraid I have not performed any measurements.

>> I have not (yet?) implemented this variant; I haven't even found a name
>> I'm happy with for it.  (seal?  plug?  cork?  another leak antonym?)

> For this improvement, I am still thinking no need to add a new mode,
> just add it by default?

Even if it is default, it may still need a name to appear before
e.g. '-gpr'.  'default-gpr' might do, but I'm not happy with it either.
  
Qing Zhao Oct. 27, 2022, 1:30 p.m. UTC | #5
> On Oct 26, 2022, at 5:29 PM, Alexandre Oliva <oliva@adacore.com> wrote:
> 
> On Oct 25, 2022, Qing Zhao <qing.zhao@oracle.com> wrote:
> 
>>> 'all' for leaf functions is likely wasteful.  If no other functions are
>>> called, one can determine exactly which registers might carry
>>> information out and thus need zeroing, and 'used' is thus likely enough,
>>> depending on the purpose of register scrubbing.  (In some scenarios, it
>>> might make sense to want scrubbing of all registers, even unused ones
>>> that carry incoming values)
> 
>> Under what kinds of situations, we should clear the un-used registers
>> for leaf functions?
> 
> The one (admittedly contrived) scenario that comes to mind is calling an
> out-of-line, empty leaf function with 'all' for explicit register
> scrubbing at desired points in the program.  I.e., this empty leaf
> function would be in charge of scrubbing the caller's registers.  It
> could even be tail-called.
> 
> I'm sure there are other scenarios in which keeping at least the
> possibility of 'all' is useful.
Okay.

> 
>> Now I am wondering whether we should make “leafy” mode by default then?
> 
> I'm not sure what you mean by default.  I think "skip" is the right
> default for general use, where register scrubbing is not explicitly
> requested.  When it is, perhaps -fzero-call-used-regs without
> '=<choice>' could be 'leafy' indeed or, even better, the extended form
> thereof that is in search of a name and so far unimplemented.

I guess that I was not clear in the previous email with the “by default”.
My previous point was:

If there is no need to clear the un-used registers for leaf functions, we can make the following change:

+  if ((zero_regs_type & LEAFY_MODE) && leaf_function_p ())
+    only_used = true;
+

As 

+  if ( leaf_function_p ())
+    only_used = true;
+

i.e, instead introducing a new MODE “LEAFY_MODE” and a new user sub-option, for LEAF functions, only
Clear its’ used registers even for “ALL”.

However, since there is need to clear the un-used registers for leaf functions. It looks like it is needed to provide
This new sub-option to users.

Is this clear this time?

> 
>> Another thing is, do you have any information on how much this new mode can save the 
>> code size and run-time compared to mode “all”?
> 
> I'm afraid I have not performed any measurements.

The major purpose of this new mode is to provide some improvement for the run-time and code-size. So, I think that 
Some information on this will be very helpful. Just a suggestion.


Another suggestion is: If this new mode is decided to add into GCC, the documentation might need to add more details on what’s the LEAFY mode,
The purpose of it, and how to use it, provide more details to the end-users
> 
>>> I have not (yet?) implemented this variant; I haven't even found a name
>>> I'm happy with for it.  (seal?  plug?  cork?  another leak antonym?)
> 
>> For this improvement, I am still thinking no need to add a new mode,
>> just add it by default?
> 
> Even if it is default, it may still need a name to appear before
> e.g. '-gpr'.  'default-gpr' might do, but I'm not happy with it either.

Default here, also means, no-need to introduce a user sub option, just add an optimization to the compiler. -:)

Qing
> 
> 
> -- 
> Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
>   Free Software Activist                       GNU Toolchain Engineer
> Disinformation flourishes because many people care deeply about injustice
> but very few check the facts.  Ask me about <https://stallmansupport.org>
  
Alexandre Oliva June 16, 2023, 7:26 a.m. UTC | #6
Hello, Qing,

On Oct 27, 2022, Qing Zhao <qing.zhao@oracle.com> wrote:
<https://gcc.gnu.org/pipermail/gcc-patches/2022-October/604480.html>

> On Oct 26, 2022, at 5:29 PM, Alexandre Oliva <oliva@adacore.com> wrote:
>> I'm sure there are other scenarios in which keeping at least the
>> possibility of 'all' is useful.
> Okay.


> i.e, instead introducing a new MODE “LEAFY_MODE” and a new user
> sub-option, for LEAF functions, only
> Clear its’ used registers even for “ALL”.

> However, since there is need to clear the un-used registers for leaf
> functions. It looks like it is needed to provide
> This new sub-option to users.

> Is this clear this time?

Yeah, I guess I understand what you mean.  But since there are cases in
which clearing all (call-clobbered) registers in a leaf function is
useful, I suppose it makes sense to offer both possibilities.

If there was a default operation mode for -fzero-call-used-regs, I guess
it would make sense to consider leafy the default, rather than all, but
since there isn't, and it always has to be specified explicitly, that's
not something to be considered.

So the available choices are:

1. introduce 'leafy' as a separate mode, leaving 'all' alone

2. change the behavior of 'all' to that of the proposed 'leafy', and either

2.a) add another mode that retains the currently-useful behavior of 'all',
   or

2.b) make the current behavior of 'all' no longer available

Personally, I find 1. the least disruptive to existing users of
-fzero-call-used-regs.  If we were introducing the option now, maybe 2.a
would be more sensible, but at this point, changing the meaning of 'all'
seems to be a disservice to security-sensitive users.

Those who would prefer the leaner operation on leaf functions can then
switch to 'leafy' mode, but that's better than finding carefully-crafted
code relying on the current behavior of 'all' for security suddenly
changes from under them, isn't it?


That said, I'm willing to implement the alternate change, if changing
the expected behavior is preferred over offering a different choice, if
needed to get closure on this feature.

For now, I'm just pinging the refreshed and retested patch.
Ok to install?


Add leafy mode for zero-call-used-regs

Introduce 'leafy' to auto-select between 'used' and 'all' for leaf and
nonleaf functions, respectively.


for  gcc/ChangeLog

	* doc/extend.texi (zero-call-used-regs): Document leafy and
	variants thereof.
	* flag-types.h (zero_regs_flags): Add LEAFY_MODE, as well as
	LEAFY and variants.
	* function.cc (gen_call_ued_regs_seq): Set only_used for leaf
	functions in leafy mode.
	* opts.cc (zero_call_used_regs_opts): Add leafy and variants.

for  gcc/testsuite/ChangeLog

	* c-c++-common/zero-scratch-regs-leafy-1.c: New.
	* c-c++-common/zero-scratch-regs-leafy-2.c: New.
	* gcc.target/i386/zero-scratch-regs-leafy-1.c: New.
	* gcc.target/i386/zero-scratch-regs-leafy-2.c: New.
---
 gcc/doc/extend.texi                                |   22 ++++++++++++++++++--
 gcc/flag-types.h                                   |    5 +++++
 gcc/function.cc                                    |    3 +++
 gcc/opts.cc                                        |    4 ++++
 .../c-c++-common/zero-scratch-regs-leafy-1.c       |   15 ++++++++++++++
 .../c-c++-common/zero-scratch-regs-leafy-2.c       |   21 +++++++++++++++++++
 .../gcc.target/i386/zero-scratch-regs-leafy-1.c    |   12 +++++++++++
 .../gcc.target/i386/zero-scratch-regs-leafy-2.c    |   16 +++++++++++++++
 8 files changed, 96 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
 create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c

diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 7b5592502734e..f8b0bb53ef5d4 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4412,10 +4412,28 @@ zeros all call-used registers that pass arguments.
 @item all-gpr-arg
 zeros all call-used general purpose registers that pass
 arguments.
+
+@item leafy
+Same as @samp{used} in a leaf function, and same as @samp{all} in a
+nonleaf function.
+
+@item leafy-gpr
+Same as @samp{used-gpr} in a leaf function, and same as @samp{all-gpr}
+in a nonleaf function.
+
+@item leafy-arg
+Same as @samp{used-arg} in a leaf function, and same as @samp{all-arg}
+in a nonleaf function.
+
+@item leafy-gpr-arg
+Same as @samp{used-gpr-arg} in a leaf function, and same as
+@samp{all-gpr-arg} in a nonleaf function.
+
 @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.
+Of this list, @samp{used-arg}, @samp{used-gpr-arg}, @samp{leafy-arg},
+@samp{leafy-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
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index f83d165fbfef1..6a2e1beb997ef 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -348,6 +348,7 @@ namespace zero_regs_flags {
   const unsigned int ONLY_GPR = 1UL << 2;
   const unsigned int ONLY_ARG = 1UL << 3;
   const unsigned int ENABLED = 1UL << 4;
+  const unsigned int LEAFY_MODE = 1UL << 5;
   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;
@@ -356,6 +357,10 @@ namespace zero_regs_flags {
   const unsigned int ALL_GPR = ENABLED | ONLY_GPR;
   const unsigned int ALL_ARG = ENABLED | ONLY_ARG;
   const unsigned int ALL = ENABLED;
+  const unsigned int LEAFY_GPR_ARG = ENABLED | LEAFY_MODE | ONLY_GPR | ONLY_ARG;
+  const unsigned int LEAFY_GPR = ENABLED | LEAFY_MODE | ONLY_GPR;
+  const unsigned int LEAFY_ARG = ENABLED | LEAFY_MODE | ONLY_ARG;
+  const unsigned int LEAFY = ENABLED | LEAFY_MODE;
 }
 
 /* Settings of flag_incremental_link.  */
diff --git a/gcc/function.cc b/gcc/function.cc
index 82102ed78d7e6..7b03f9e744199 100644
--- a/gcc/function.cc
+++ b/gcc/function.cc
@@ -5868,6 +5868,9 @@ gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type)
   only_used = zero_regs_type & ONLY_USED;
   only_arg = zero_regs_type & ONLY_ARG;
 
+  if ((zero_regs_type & LEAFY_MODE) && leaf_function_p ())
+    only_used = true;
+
   /* 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;
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 86b94d62b588c..93c78be8b0d9a 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -2114,6 +2114,10 @@ const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
   ZERO_CALL_USED_REGS_OPT (all-gpr, zero_regs_flags::ALL_GPR),
   ZERO_CALL_USED_REGS_OPT (all-arg, zero_regs_flags::ALL_ARG),
   ZERO_CALL_USED_REGS_OPT (all, zero_regs_flags::ALL),
+  ZERO_CALL_USED_REGS_OPT (leafy-gpr-arg, zero_regs_flags::LEAFY_GPR_ARG),
+  ZERO_CALL_USED_REGS_OPT (leafy-gpr, zero_regs_flags::LEAFY_GPR),
+  ZERO_CALL_USED_REGS_OPT (leafy-arg, zero_regs_flags::LEAFY_ARG),
+  ZERO_CALL_USED_REGS_OPT (leafy, zero_regs_flags::LEAFY),
 #undef ZERO_CALL_USED_REGS_OPT
   {NULL, 0U}
 };
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
new file mode 100644
index 0000000000000..c1a0c31ba1c37
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
@@ -0,0 +1,15 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=leafy" } */
+
+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-leafy-2.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
new file mode 100644
index 0000000000000..d450620c1fcfe
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <assert.h>
+int result = 0;
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("leafy")))
+foo1 (int x)
+{
+  return (x + 1);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("leafy")))
+foo2 (int x)
+{
+  return foo1 (x + 2);
+}
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
new file mode 100644
index 0000000000000..2277710c771b7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fzero-call-used-regs=leafy -fno-stack-protector -fno-PIC" } */
+
+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-leafy-2.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
new file mode 100644
index 0000000000000..24b85c3dbb766
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fzero-call-used-regs=leafy-gpr -fno-stack-protector -fno-PIC" } */
+
+extern int bar (int);
+
+void
+foo (void)
+{
+  int x = bar (0);
+  if (x)
+    bar (1);
+}
+
+/* { dg-final { scan-assembler "xorl\[ \t\]+%eax, %eax" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]+%edx, %edx" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]+%ecx, %ecx" } } */
  
Qing Zhao June 16, 2023, 7:34 p.m. UTC | #7
Hi, Alexandre,

> On Jun 16, 2023, at 3:26 AM, Alexandre Oliva <oliva@adacore.com> wrote:
> 
> Hello, Qing,
> 
> On Oct 27, 2022, Qing Zhao <qing.zhao@oracle.com> wrote:
> <https://gcc.gnu.org/pipermail/gcc-patches/2022-October/604480.html>
> 
>> On Oct 26, 2022, at 5:29 PM, Alexandre Oliva <oliva@adacore.com> wrote:
>>> I'm sure there are other scenarios in which keeping at least the
>>> possibility of 'all' is useful.
>> Okay.
> 
> 
>> i.e, instead introducing a new MODE “LEAFY_MODE” and a new user
>> sub-option, for LEAF functions, only
>> Clear its’ used registers even for “ALL”.
> 
>> However, since there is need to clear the un-used registers for leaf
>> functions. It looks like it is needed to provide
>> This new sub-option to users.
> 
>> Is this clear this time?
> 
> Yeah, I guess I understand what you mean.  But since there are cases in
> which clearing all (call-clobbered) registers in a leaf function is
> useful, I suppose it makes sense to offer both possibilities.
agreed.
> 
> If there was a default operation mode for -fzero-call-used-regs, I guess
> it would make sense to consider leafy the default, rather than all, but
> since there isn't, and it always has to be specified explicitly, that's
> not something to be considered.
> 
> So the available choices are:
> 
> 1. introduce 'leafy' as a separate mode, leaving 'all' alone
> 
> 2. change the behavior of 'all' to that of the proposed 'leafy', and either
> 
> 2.a) add another mode that retains the currently-useful behavior of 'all',
>   or
> 
> 2.b) make the current behavior of 'all' no longer available
> 
> Personally, I find 1. the least disruptive to existing users of
> -fzero-call-used-regs.  If we were introducing the option now, maybe 2.a
> would be more sensible, but at this point, changing the meaning of 'all'
> seems to be a disservice to security-sensitive users.
> 
> Those who would prefer the leaner operation on leaf functions can then
> switch to 'leafy' mode, but that's better than finding carefully-crafted
> code relying on the current behavior of 'all' for security suddenly
> changes from under them, isn't it?

Yes, I agree.
> 
> 
> That said, I'm willing to implement the alternate change, if changing
> the expected behavior is preferred over offering a different choice, if
> needed to get closure on this feature.
> 
> For now, I'm just pinging the refreshed and retested patch.

As I mentioned in the previous round of review, I think that the documentation
 might need to add more details on what’s the LEAFY mode,
The purpose of it, and how to use it, provide more details to the end-users.


> Ok to install?
> 
> 
> Add leafy mode for zero-call-used-regs
> 
> Introduce 'leafy' to auto-select between 'used' and 'all' for leaf and
> nonleaf functions, respectively.
> 
> 
> for  gcc/ChangeLog
> 
> 	* doc/extend.texi (zero-call-used-regs): Document leafy and
> 	variants thereof.
> 	* flag-types.h (zero_regs_flags): Add LEAFY_MODE, as well as
> 	LEAFY and variants.
> 	* function.cc (gen_call_ued_regs_seq): Set only_used for leaf
> 	functions in leafy mode.
> 	* opts.cc (zero_call_used_regs_opts): Add leafy and variants.
> 
> for  gcc/testsuite/ChangeLog
> 
> 	* c-c++-common/zero-scratch-regs-leafy-1.c: New.
> 	* c-c++-common/zero-scratch-regs-leafy-2.c: New.
> 	* gcc.target/i386/zero-scratch-regs-leafy-1.c: New.
> 	* gcc.target/i386/zero-scratch-regs-leafy-2.c: New.
> ---
> gcc/doc/extend.texi                                |   22 ++++++++++++++++++--
> gcc/flag-types.h                                   |    5 +++++
> gcc/function.cc                                    |    3 +++
> gcc/opts.cc                                        |    4 ++++
> .../c-c++-common/zero-scratch-regs-leafy-1.c       |   15 ++++++++++++++
> .../c-c++-common/zero-scratch-regs-leafy-2.c       |   21 +++++++++++++++++++
> .../gcc.target/i386/zero-scratch-regs-leafy-1.c    |   12 +++++++++++
> .../gcc.target/i386/zero-scratch-regs-leafy-2.c    |   16 +++++++++++++++
> 8 files changed, 96 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
> create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
> 
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 7b5592502734e..f8b0bb53ef5d4 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi

I think in the documentation of zero_call_used_regs, 

After the description of the 3 basic values: “skip”, “used”, and “all”. 
The description of the new value “leafy” need to be added first.

In addition to the below doc change.

The others LGTM.

Thanks.

Qing

> @@ -4412,10 +4412,28 @@ zeros all call-used registers that pass arguments.
> @item all-gpr-arg
> zeros all call-used general purpose registers that pass
> arguments.
> +
> +@item leafy
> +Same as @samp{used} in a leaf function, and same as @samp{all} in a
> +nonleaf function.
> +
> +@item leafy-gpr
> +Same as @samp{used-gpr} in a leaf function, and same as @samp{all-gpr}
> +in a nonleaf function.
> +
> +@item leafy-arg
> +Same as @samp{used-arg} in a leaf function, and same as @samp{all-arg}
> +in a nonleaf function.
> +
> +@item leafy-gpr-arg
> +Same as @samp{used-gpr-arg} in a leaf function, and same as
> +@samp{all-gpr-arg} in a nonleaf function.
> +
> @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.
> +Of this list, @samp{used-arg}, @samp{used-gpr-arg}, @samp{leafy-arg},
> +@samp{leafy-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
> diff --git a/gcc/flag-types.h b/gcc/flag-types.h
> index f83d165fbfef1..6a2e1beb997ef 100644
> --- a/gcc/flag-types.h
> +++ b/gcc/flag-types.h
> @@ -348,6 +348,7 @@ namespace zero_regs_flags {
>   const unsigned int ONLY_GPR = 1UL << 2;
>   const unsigned int ONLY_ARG = 1UL << 3;
>   const unsigned int ENABLED = 1UL << 4;
> +  const unsigned int LEAFY_MODE = 1UL << 5;
>   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;
> @@ -356,6 +357,10 @@ namespace zero_regs_flags {
>   const unsigned int ALL_GPR = ENABLED | ONLY_GPR;
>   const unsigned int ALL_ARG = ENABLED | ONLY_ARG;
>   const unsigned int ALL = ENABLED;
> +  const unsigned int LEAFY_GPR_ARG = ENABLED | LEAFY_MODE | ONLY_GPR | ONLY_ARG;
> +  const unsigned int LEAFY_GPR = ENABLED | LEAFY_MODE | ONLY_GPR;
> +  const unsigned int LEAFY_ARG = ENABLED | LEAFY_MODE | ONLY_ARG;
> +  const unsigned int LEAFY = ENABLED | LEAFY_MODE;
> }
> 
> /* Settings of flag_incremental_link.  */
> diff --git a/gcc/function.cc b/gcc/function.cc
> index 82102ed78d7e6..7b03f9e744199 100644
> --- a/gcc/function.cc
> +++ b/gcc/function.cc
> @@ -5868,6 +5868,9 @@ gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type)
>   only_used = zero_regs_type & ONLY_USED;
>   only_arg = zero_regs_type & ONLY_ARG;
> 
> +  if ((zero_regs_type & LEAFY_MODE) && leaf_function_p ())
> +    only_used = true;
> +
>   /* 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;
> diff --git a/gcc/opts.cc b/gcc/opts.cc
> index 86b94d62b588c..93c78be8b0d9a 100644
> --- a/gcc/opts.cc
> +++ b/gcc/opts.cc
> @@ -2114,6 +2114,10 @@ const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
>   ZERO_CALL_USED_REGS_OPT (all-gpr, zero_regs_flags::ALL_GPR),
>   ZERO_CALL_USED_REGS_OPT (all-arg, zero_regs_flags::ALL_ARG),
>   ZERO_CALL_USED_REGS_OPT (all, zero_regs_flags::ALL),
> +  ZERO_CALL_USED_REGS_OPT (leafy-gpr-arg, zero_regs_flags::LEAFY_GPR_ARG),
> +  ZERO_CALL_USED_REGS_OPT (leafy-gpr, zero_regs_flags::LEAFY_GPR),
> +  ZERO_CALL_USED_REGS_OPT (leafy-arg, zero_regs_flags::LEAFY_ARG),
> +  ZERO_CALL_USED_REGS_OPT (leafy, zero_regs_flags::LEAFY),
> #undef ZERO_CALL_USED_REGS_OPT
>   {NULL, 0U}
> };
> diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
> new file mode 100644
> index 0000000000000..c1a0c31ba1c37
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
> @@ -0,0 +1,15 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fzero-call-used-regs=leafy" } */
> +
> +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-leafy-2.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
> new file mode 100644
> index 0000000000000..d450620c1fcfe
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
> @@ -0,0 +1,21 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +
> +#include <assert.h>
> +int result = 0;
> +
> +int 
> +__attribute__((noipa))
> +__attribute__ ((zero_call_used_regs("leafy")))
> +foo1 (int x)
> +{
> +  return (x + 1);
> +}
> +
> +int 
> +__attribute__((noipa))
> +__attribute__ ((zero_call_used_regs("leafy")))
> +foo2 (int x)
> +{
> +  return foo1 (x + 2);
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
> new file mode 100644
> index 0000000000000..2277710c771b7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
> @@ -0,0 +1,12 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fzero-call-used-regs=leafy -fno-stack-protector -fno-PIC" } */
> +
> +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-leafy-2.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
> new file mode 100644
> index 0000000000000..24b85c3dbb766
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
> @@ -0,0 +1,16 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fzero-call-used-regs=leafy-gpr -fno-stack-protector -fno-PIC" } */
> +
> +extern int bar (int);
> +
> +void
> +foo (void)
> +{
> +  int x = bar (0);
> +  if (x)
> +    bar (1);
> +}
> +
> +/* { dg-final { scan-assembler "xorl\[ \t\]+%eax, %eax" } } */
> +/* { dg-final { scan-assembler "xorl\[ \t\]+%edx, %edx" } } */
> +/* { dg-final { scan-assembler "xorl\[ \t\]+%ecx, %ecx" } } */
> 
> 
> -- 
> Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
>   Free Software Activist                       GNU Toolchain Engineer
> Disinformation flourishes because many people care deeply about injustice
> but very few check the facts.  Ask me about <https://stallmansupport.org>
  
Alexandre Oliva June 22, 2023, 1:16 a.m. UTC | #8
Hello, Qing,

On Jun 16, 2023, Qing Zhao <qing.zhao@oracle.com> wrote:

> As I mentioned in the previous round of review, I think that the documentation
>  might need to add more details on what’s the LEAFY mode,
> The purpose of it, and how to use it, provide more details to the end-users.

I'm afraid I'm having difficulty picturing what it is that you're
looking for.  The proposal incorporates, by reference, all the
documentation for 'used' and for 'all', and the way to use it is no
different.

>> +Same as @samp{used} in a leaf function, and same as @samp{all} in a
>> +nonleaf function.

If there was documentation on how to choose between e.g. all and used, I
suppose I could build on that to add this intermediate choice, but...  I
can't find any such docs, and I'm uncertain on whether adding that would
be useful to begin with.

Did you have something else in mind?
  
Qing Zhao June 23, 2023, 2:47 p.m. UTC | #9
Hi, Alexandre,


> On Jun 21, 2023, at 9:16 PM, Alexandre Oliva <oliva@adacore.com> wrote:
> 
> Hello, Qing,
> 
> On Jun 16, 2023, Qing Zhao <qing.zhao@oracle.com> wrote:
> 
>> As I mentioned in the previous round of review, I think that the documentation
>> might need to add more details on what’s the LEAFY mode,
>> The purpose of it, and how to use it, provide more details to the end-users.
> 
> I'm afraid I'm having difficulty picturing what it is that you're
> looking for.  The proposal incorporates, by reference, all the
> documentation for 'used' and for 'all', and the way to use it is no
> different.
> 
>>> +Same as @samp{used} in a leaf function, and same as @samp{all} in a
>>> +nonleaf function.

Oh, yeah.  The definition of “leafy” is here. -:)

It’s better to add this definition earlier in the list of the “three basic values”, to make it “four basic values”, like the following:

=======
In order to satisfy users with different security needs and control the
run-time overhead at the same time, the @var{choice} parameter provides a
flexible way to choose the subset of the call-used registers to be zeroed.
The four 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

@item
@samp{leafy} Same as @samp{used} in a leaf function, and same as @samp{all} in a
    nonleaf function. This value is mainly to provide users a more efficient mode to zero 
    call-used registers in leaf functions.
@end itemize
======

Then,  in the full list of choice, add the new values of leafy, leafy-gpr, leafy-arg, leafy-gpr-arg 

The sentence "This value is mainly to provide users a more efficient mode to zero 
    call-used registers in leaf functions.” just for your reference,  the wording can certainly be improved.  -:)
> 
> If there was documentation on how to choose between e.g. all and used, I
> suppose I could build on that to add this intermediate choice, but...  I
> can't find any such docs, and I'm uncertain on whether adding that would
> be useful to begin with.
> 
> Did you have something else in mind?


Hope this time I am clear (and sorry for the confusion in the previous emails).

thanks.

Qing

> 
> -- 
> Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
>   Free Software Activist                       GNU Toolchain Engineer
> Disinformation flourishes because many people care deeply about injustice
> but very few check the facts.  Ask me about <https://stallmansupport.org>
  

Patch

diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 04af0584d82cc..bf11956c467fb 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4391,10 +4391,28 @@  zeros all call-used registers that pass arguments.
 @item all-gpr-arg
 zeros all call-used general purpose registers that pass
 arguments.
+
+@item leafy
+Same as @samp{used} in a leaf function, and same as @samp{all} in a
+nonleaf function.
+
+@item leafy-gpr
+Same as @samp{used-gpr} in a leaf function, and same as @samp{all-gpr}
+in a nonleaf function.
+
+@item leafy-arg
+Same as @samp{used-arg} in a leaf function, and same as @samp{all-arg}
+in a nonleaf function.
+
+@item leafy-gpr-arg
+Same as @samp{used-gpr-arg} in a leaf function, and same as
+@samp{all-gpr-arg} in a nonleaf function.
+
 @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.
+Of this list, @samp{used-arg}, @samp{used-gpr-arg}, @samp{leafy-arg},
+@samp{leafy-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
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index d2e751060ffce..b90c85167dcd4 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -338,6 +338,7 @@  namespace zero_regs_flags {
   const unsigned int ONLY_GPR = 1UL << 2;
   const unsigned int ONLY_ARG = 1UL << 3;
   const unsigned int ENABLED = 1UL << 4;
+  const unsigned int LEAFY_MODE = 1UL << 5;
   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;
@@ -346,6 +347,10 @@  namespace zero_regs_flags {
   const unsigned int ALL_GPR = ENABLED | ONLY_GPR;
   const unsigned int ALL_ARG = ENABLED | ONLY_ARG;
   const unsigned int ALL = ENABLED;
+  const unsigned int LEAFY_GPR_ARG = ENABLED | LEAFY_MODE | ONLY_GPR | ONLY_ARG;
+  const unsigned int LEAFY_GPR = ENABLED | LEAFY_MODE | ONLY_GPR;
+  const unsigned int LEAFY_ARG = ENABLED | LEAFY_MODE | ONLY_ARG;
+  const unsigned int LEAFY = ENABLED | LEAFY_MODE;
 }
 
 /* Settings of flag_incremental_link.  */
diff --git a/gcc/function.cc b/gcc/function.cc
index 6474a663b30b8..16582e698041a 100644
--- a/gcc/function.cc
+++ b/gcc/function.cc
@@ -5879,6 +5879,9 @@  gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type)
   only_used = zero_regs_type & ONLY_USED;
   only_arg = zero_regs_type & ONLY_ARG;
 
+  if ((zero_regs_type & LEAFY_MODE) && leaf_function_p ())
+    only_used = true;
+
   /* 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;
diff --git a/gcc/opts.cc b/gcc/opts.cc
index ae079fcd20eea..39f6a1b278dc6 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -2099,6 +2099,10 @@  const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
   ZERO_CALL_USED_REGS_OPT (all-gpr, zero_regs_flags::ALL_GPR),
   ZERO_CALL_USED_REGS_OPT (all-arg, zero_regs_flags::ALL_ARG),
   ZERO_CALL_USED_REGS_OPT (all, zero_regs_flags::ALL),
+  ZERO_CALL_USED_REGS_OPT (leafy-gpr-arg, zero_regs_flags::LEAFY_GPR_ARG),
+  ZERO_CALL_USED_REGS_OPT (leafy-gpr, zero_regs_flags::LEAFY_GPR),
+  ZERO_CALL_USED_REGS_OPT (leafy-arg, zero_regs_flags::LEAFY_ARG),
+  ZERO_CALL_USED_REGS_OPT (leafy, zero_regs_flags::LEAFY),
 #undef ZERO_CALL_USED_REGS_OPT
   {NULL, 0U}
 };
diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
new file mode 100644
index 0000000000000..c1a0c31ba1c37
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-1.c
@@ -0,0 +1,15 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fzero-call-used-regs=leafy" } */
+
+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-leafy-2.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
new file mode 100644
index 0000000000000..d450620c1fcfe
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-leafy-2.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <assert.h>
+int result = 0;
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("leafy")))
+foo1 (int x)
+{
+  return (x + 1);
+}
+
+int 
+__attribute__((noipa))
+__attribute__ ((zero_call_used_regs("leafy")))
+foo2 (int x)
+{
+  return foo1 (x + 2);
+}
diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
new file mode 100644
index 0000000000000..2277710c771b7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-1.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fzero-call-used-regs=leafy -fno-stack-protector -fno-PIC" } */
+
+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-leafy-2.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
new file mode 100644
index 0000000000000..24b85c3dbb766
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-leafy-2.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fzero-call-used-regs=leafy-gpr -fno-stack-protector -fno-PIC" } */
+
+extern int bar (int);
+
+void
+foo (void)
+{
+  int x = bar (0);
+  if (x)
+    bar (1);
+}
+
+/* { dg-final { scan-assembler "xorl\[ \t\]+%eax, %eax" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]+%edx, %edx" } } */
+/* { dg-final { scan-assembler "xorl\[ \t\]+%ecx, %ecx" } } */