> On Oct 26, 2020, at 1:47 PM, Richard Sandiford <richard.sandif...@arm.com>
> wrote:
>
> Qing Zhao <qing.z...@oracle.com> writes:
>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>> index c9f7299..3a884e1 100644
>> --- a/gcc/doc/extend.texi
>> +++ b/gcc/doc/extend.texi
>> @@ -3992,6 +3992,49 @@ performing a link with relocatable output (i.e.@:
>> @code{ld -r}) on them.
>> A declaration to which @code{weakref} is attached and that is associated
>> with a named @code{target} must be @code{static}.
>>
>> +@item zero_call_used_regs ("@var{choice}")
>> +@cindex @code{zero_call_used_regs} function attribute
>> +
>> +The @code{zero_call_used_regs} attribute causes the compiler to zero
>> +a subset of all call-used registers at function return according to
>> +@var{choice}.
>> +This is used to increase the program security by either mitigating
>> +Return-Oriented Programming (ROP) or preventing information leak
>> +through registers.
>> +
>> +A "call-used" register is a register that is clobbered by function calls,
>> +as a result, the caller has to save and restore it before or after a
>> +function call. It is also called as "call-clobbered", "caller-saved", or
>> +"volatile".
>
> texinfo quoting is to use ``…'' rather than "…". So maybe:
>
> -------------------------------------------------------------------
> 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''.
> —————————————————————————————————
Okay.
>
>> +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
>
> nit: should only be one space after the comma
Okay.
>
>> +the subset of the call-used registers to be zeroed.
>
> Maybe add “The three basic values of @var{choice} are:”
Yes, This sounds better.
>
>> +
>> +@samp{skip} doesn't zero any call-used registers.
>> +@samp{used} zeros call-used registers which are used in the function. A
>> "used"
>
> Maybe s/zeros/only zeros/?
>
> s/which/that/
okay.
>
>> +register is one whose content has been set or referenced in the function.
>> +@samp{all} zeros all call-used registers.
>
> I think this would be better formatted using a @table.
>
>> +In addition to the above three basic choices, the register set can be
>> further
>> +limited by adding "-gpr" (i.e., general purpose register), "-arg" (i.e.,
>> +argument register), or both as following:
>
> How about:
>
> -------------------------------------------------------------------
> 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 are used
> to pass parameters. When applied to @samp{all}, this includes all
> parameter registers defined by the platform's calling convention,
> regardless of whether the function uses those parameter registers.
> @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:
> -------------------------------------------------------------------
>
> with the list repeating @var{skip}, @var{used} and @var{all}.
>
> (untested)
Okay, I will update and test this.
>
>> +@samp{used-gpr-arg} zeros used call-used general purpose registers that
>> +pass parameters.
>> +@samp{used-arg} zeros used call-used registers that pass parameters.
>> +@samp{all-gpr-arg} zeros all call-used general purpose registers that pass
>> +parameters.
>> +@samp{all-arg} zeros all call-used registers that pass parameters.
>> +@samp{used-gpr} zeros call-used general purpose registers which are used in
>> the
>> +function.
>> +@samp{all-gpr} zeros all call-used general purpose registers.
>
> I think this too should be a @table.
Okay.
>
>> +
>> +Among this list, "used-gpr-arg", "used-arg", "all-gpr-arg", and "all-arg"
>> are
>> +mainly used for ROP mitigation.
>
> Should be quoted using @samp rather than “.
Okay.
>
>> +@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.
>
> After this, we should probably say something like:
>
> -------------------------------------------------------------------
> The possible values of @var{choice} are the same as for the
> @samp{zero_call_used_regs} attribute (@pxref{…}). The default
> is @samp{skip}.
> -------------------------------------------------------------------
>
> (with the xref filled in)
Okay.
>
>> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
>> index 97437e8..3b75c46 100644
>> --- a/gcc/doc/tm.texi
>> +++ b/gcc/doc/tm.texi
>> @@ -12053,6 +12053,18 @@ argument list due to stack realignment. Return
>> @code{NULL} if no DRAP
>> is needed.
>> @end deftypefn
>>
>> +@deftypefn {Target Hook} HARD_REG_SET TARGET_ZERO_CALL_USED_REGS
>> (HARD_REG_SET @var{selected_regs})
>> +This target hook emits instructions to zero subset of @var{selected_regs}
>
> …to zero the subset…
> (probably my mistake, sorry)
Okay. (My mistake, I should check it myself..)
>
>> diff --git a/gcc/flag-types.h b/gcc/flag-types.h
>> index 852ea76..0f7e503 100644
>> --- a/gcc/flag-types.h
>> +++ b/gcc/flag-types.h
>> @@ -285,6 +285,15 @@ enum sanitize_code {
>> | SANITIZE_BOUNDS_STRICT
>> };
>>
>> +enum zero_call_used_regs_code {
>> + UNSET = 0,
>> + SKIP = 1UL << 0,
>> + ONLY_USED = 1UL << 1,
>> + ONLY_GPR = 1UL << 2,
>> + ONLY_ARG = 1UL << 3,
>> + ALL = 1UL << 4
>> +};
>
> I'd suggested these names on the assumption that we'd be using
> a C++ enum class, so that the enum would be referenced as
> name::ALL, name::SKIP, etc. But I guess using a C++ enum class
> doesn't work well with bitfields after all.
>
> These names are too generic without the name:: scoping though.
> Perhaps we should put them in a namespace:
>
> namespace zero_regs_flags {
> const unsigned int UNSET = 0;
> …etc…
> }
>
> (call-used probably doesn't need to be part of the flag names,
> since the concept is more general than that and call-usedness
> is really a filter that's being applied on top. Although I guess
> the same is true of “zero”. ;-))
>
> I don't think we should have ALL as a separate flag: ALL is the absence
> of ONLY_*. Maybe we should have an ENABLED flag that all non-skip
> combinations use?
>
> If it makes things easier, I think it would be good to have e.g.:
>
> unsigned int USED_GPR = ENABLED | ONLY_USED | ONLY_GPR;
>
> inside the namespace, to reduce the verbosity in the option table.
Then, the final namespace will look like:
namespace zero_regs_flags {
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 = 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_GRP_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;
}
??
>
>> + /* If gpr_only is true, only zero call-used-registers that are
>> + general-purpose registers; if used_only is true, only zero
>> + call-used-registers that are used in the current function. */
>> +
>> + gpr_only = crtl->zero_call_used_regs & ONLY_GPR;
>> + used_only = crtl->zero_call_used_regs & ONLY_USED;
>> + arg_only = crtl->zero_call_used_regs & ONLY_ARG;
>> +
>> + /* For each of the hard registers, check to see whether we should zero it
>> if:
>
> s/check to see whether //
Okay.
>
>> + 1. it is a call-used-registers;
>
> s/call-used-registers/call-used register/
Okay.
>
>> + and 2. it is not a fixed-registers;
>
> s/fixed-registers/fixed register/
Okay.
>
>> + 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;
>> + */
>
> Under GCC formatting, the “and” lines need to be indented under “For each”.
> Maybe indent the “1.” line a bit more if you think it looks nicer with the
> numbers lined up (it probably does).
>
> Similarly, the last bit of text should end with “. */”, rather than
> with the “;\n */” above.
>
> (Sorry that the rules are so picky about this.)
/* For each of the hard registers, check to see whether we should zero it if:
1. it is a call-used-registers;
and 2. it is not a fixed-registers;
and 3. it is not live at the return of the routine;
and 4. it is general registor if gpr_only is true;
and 5. it is used in the routine if used_only is true;
and 6. it is a register that passes parameter if arg_only is true. */
How about this?
>
>> + /* First, prepare the data flow information. */
>> + basic_block bb = BLOCK_FOR_INSN (ret);
>> + bitmap live_out;
>> + live_out = BITMAP_ALLOC (NULL);
>
> Should just use auto_bitmap here, which will also handle the freeing.
I remembered that I used auto_bitmap initially, but it didn’t work, so I
changed it like this.
I will try to see whether it work.
>
>> + bitmap_copy (live_out, df_get_live_out (bb));
>> + df_simulate_initialize_backwards (bb, live_out);
>> + df_simulate_one_insn_backwards (bb, ret, live_out);
>> +
>> + HARD_REG_SET need_zeroed_hardregs;
>> + CLEAR_HARD_REG_SET (need_zeroed_hardregs);
>
> Maybe s/need_zeroed/selected/? Similarly to the target hook comment
> in the previous review, I think “need” makes it sound like the target
> has no freedom to decline.
Okay.
>
>> + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
>> + {
>> + if (!crtl->abi->clobbers_full_reg_p (regno))
>> + continue;
>> + if (fixed_regs[regno])
>> + continue;
>> + if (REGNO_REG_SET_P (live_out, regno))
>> + continue;
>> + if (gpr_only
>> + && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
>> + continue;
>> + if (used_only && !df_regs_ever_live_p (regno))
>> + continue;
>> + if (arg_only && !FUNCTION_ARG_REGNO_P (regno))
>> + continue;
>> +
>> + /* Now this is a register that we might want to zero. */
>> + SET_HARD_REG_BIT (need_zeroed_hardregs, regno);
>> + }
>> +
>> + BITMAP_FREE (live_out);
>> +
>> + if (hard_reg_set_empty_p (need_zeroed_hardregs))
>> + return;
>> +
>> + /* Now we get a hard register set that need to be zeroed, pass it to
>> + target to generate zeroing sequence. */
>
> /* Now that we have a hard register set that needs to be zeroed, pass it
> to the target to generate the zeroing sequence. */
Okay.
>
>> + HARD_REG_SET zeroed_hardregs;
>> + start_sequence ();
>> + zeroed_hardregs = targetm.calls.zero_call_used_regs
>> (need_zeroed_hardregs);
>> + rtx_insn *seq = get_insns ();
>> + end_sequence ();
>> + if (seq)
>> + {
>> + /* Emit the memory blockage and register clobber asm volatile before
>> + the whole sequence. */
>> + start_sequence ();
>> + expand_asm_reg_clobber_mem_blockage (zeroed_hardregs);
>> + rtx_insn *seq_barrier = get_insns ();
>> + end_sequence ();
>> +
>> + emit_insn_before (seq_barrier, ret);
>> + emit_insn_before (seq, ret);
>> +
>> + /* Update the data flow information. */
>> + crtl->must_be_zero_on_return |= zeroed_hardregs;
>> + df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun));
>> + }
>> +}
>> +
>> +
>> /* Return a sequence to be used as the epilogue for the current function,
>> or NULL. */
>>
>> @@ -6486,7 +6584,120 @@ make_pass_thread_prologue_and_epilogue (gcc::context
>> *ctxt)
>> {
>> return new pass_thread_prologue_and_epilogue (ctxt);
>> }
>> -
>>
>> +
>> +static unsigned int
>> +rest_of_zero_call_used_regs (void)
>
> This needs a function comment. Maybe:
>
> /* 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. */
>
> Also, we might as well make it:
>
> pass_zero_call_used_regs::execute
>
> rather than a separate function. The “rest_of_…” stuff is mostly legacy.
You mean to delete the “rest_of_zero_call_used_regs” function, and move its
body to
Pass_zero_call_used_regs::execute?
>
>> +{
>> + edge_iterator ei;
>> + edge e;
>> + rtx_insn *insn;
>> +
>> + /* This pass needs data flow information. */
>> + df_analyze ();
>> +
>> + /* Search all the "return"s in the routine, and insert instruction
>> sequence to
>> + zero the call used registers. */
>> + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
>> + {
>> + insn = BB_END (e->src);
>
> Modern style would be to declare insn here rather than above.
Okay.
>
>> + if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn)))
>> + gen_call_used_regs_seq (insn);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +namespace {
>> +
>> +const pass_data pass_data_zero_call_used_regs =
>> +{
>> + RTL_PASS, /* type */
>> + "zero_call_used_regs", /* name */
>> + OPTGROUP_NONE, /* optinfo_flags */
>> + TV_NONE, /* tv_id */
>> + 0, /* properties_required */
>> + 0, /* properties_provided */
>> + 0, /* properties_destroyed */
>> + 0, /* todo_flags_start */
>> + 0, /* todo_flags_finish */
>> +};
>> +
>> +class pass_zero_call_used_regs: public rtl_opt_pass
>> +{
>> +public:
>> + pass_zero_call_used_regs (gcc::context *ctxt)
>> + : rtl_opt_pass (pass_data_zero_call_used_regs, ctxt)
>> + {}
>> +
>> + /* opt_pass methods: */
>> + virtual bool gate (function *);
>> +
>> + virtual unsigned int execute (function *)
>> + {
>> + return rest_of_zero_call_used_regs ();
>> + }
>> +
>> +}; // class pass_zero_call_used_regs
>> +
>> +bool
>> +pass_zero_call_used_regs::gate (function *fun)
>> +{
>> + unsigned int zero_regs_type = UNSET;
>> + unsigned int attr_zero_regs_type = UNSET;
>> +
>> + tree attr_zero_regs
>> + = lookup_attribute ("zero_call_used_regs",
>> + DECL_ATTRIBUTES (fun->decl));
>> +
>> + /* Get the type of zero_call_used_regs from function attribute. */
>> + if (attr_zero_regs)
>> + {
>> + bool found = false;
>> + unsigned int i;
>> +
>> + /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
>> + is the attribute argument's value. */
>> + attr_zero_regs = TREE_VALUE (attr_zero_regs);
>> + gcc_assert (TREE_CODE (attr_zero_regs) == TREE_LIST);
>> + attr_zero_regs = TREE_VALUE (attr_zero_regs);
>> + gcc_assert (TREE_CODE (attr_zero_regs) == STRING_CST);
>> +
>> + for (i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
>> + if (strcmp (TREE_STRING_POINTER (attr_zero_regs),
>> + zero_call_used_regs_opts[i].name) == 0)
>> + {
>> + attr_zero_regs_type |= zero_call_used_regs_opts[i].flag;
>
> Think = is less surprising than |= here.
Yes.
>
>> + found = true;
>
> All valid values are nonzero, so we don't need a separate boolean.
Yes.
>
>> + break;
>> + }
>> +
>> + if (!found)
>> + warning_at (DECL_SOURCE_LOCATION (fun->decl), 0,
>> + "unrecognized zero_call_used_regs attribute: %qs",
>> + TREE_STRING_POINTER (attr_zero_regs));
>
> I think we should warn when handling the attribute in c-attribs.c
> (as before, IIRC), and make it silent here.
Okay. Will do that.
>
>> + }
>> +
>> + if (flag_zero_call_used_regs)
>> + if (!attr_zero_regs)
>> + zero_regs_type = flag_zero_call_used_regs;
>> + else
>> + zero_regs_type = attr_zero_regs_type;
>> + else
>> + zero_regs_type = attr_zero_regs_type;
>
> Seems easier to make the attribute code set zero_regs_type directly,
> then have:
>
> if (!zero_regs_type)
> zero_regs_type = flag_zero_call_used_regs;
okay.
>
>> +
>> + crtl->zero_call_used_regs = zero_regs_type;
>> +
>> + /* No need to zero call-used-regs when no user request is present. */
>> + return zero_regs_type > SKIP;
>
> Think testing for skip using & SKIP or ==/!= SKIP is more obvious.
Testing with & SKIP or ==/!= SKIP will not work if the flag is UNSET.
>
> This is too much for a gate function, which should be a simple
> side-effect-free function that tests whether the pass should run.
> Perhaps we should just make the pass unconditional and do the above
> in ::execute. The pass is very cheap, so gating probably isn't
> worthwhile.
Okay, I can do that.
>
>> +}
>> +
>> +} // anon namespace
>> +
>> +rtl_opt_pass *
>> +make_pass_zero_call_used_regs (gcc::context *ctxt)
>> +{
>> + return new pass_zero_call_used_regs (ctxt);
>> +}
>>
>> /* If CONSTRAINT is a matching constraint, then return its number.
>> Otherwise, return -1. */
>> diff --git a/gcc/optabs.c b/gcc/optabs.c
>> index 8ad7f4b..bd64af0 100644
>> --- a/gcc/optabs.c
>> +++ b/gcc/optabs.c
>> @@ -6484,6 +6484,48 @@ expand_memory_blockage (void)
>> expand_asm_memory_blockage ();
>> }
>>
>> +/* Generate asm volatile("" : : : "memory") as a memory blockage, at the
>> + same time clobbering the register set specified by REGS. */
>> +
>> +void
>> +expand_asm_reg_clobber_mem_blockage (HARD_REG_SET regs)
>> +{
>> + rtx asm_op, clob_mem;
>> +
>> + unsigned int num_of_regs = 0;
>> + for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
>> + if (TEST_HARD_REG_BIT (regs, i))
>> + num_of_regs++;
>> +
>> + asm_op = gen_rtx_ASM_OPERANDS (VOIDmode, "", "", 0,
>> + rtvec_alloc (0), rtvec_alloc (0),
>> + rtvec_alloc (0), UNKNOWN_LOCATION);
>> + MEM_VOLATILE_P (asm_op) = 1;
>> +
>> + rtvec v = rtvec_alloc (num_of_regs + 2);
>> +
>> + clob_mem = gen_rtx_SCRATCH (VOIDmode);
>> + clob_mem = gen_rtx_MEM (BLKmode, clob_mem);
>> + clob_mem = gen_rtx_CLOBBER (VOIDmode, clob_mem);
>> +
>> + RTVEC_ELT (v,0) = asm_op;
>> + RTVEC_ELT (v,1) = clob_mem;
>
> nit: should be a space before the comma, here and below.
Okay.
>
>> +
>> + if (num_of_regs > 0)
>> + {
>> + unsigned int j = 2;
>> + for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
>> + if (TEST_HARD_REG_BIT (regs, i))
>> + {
>> + RTVEC_ELT (v,j) = gen_rtx_CLOBBER (VOIDmode, regno_reg_rtx[i]);
>> + j++;
>> + }
>> + gcc_assert (j == (num_of_regs + 2));
>> + }
>> +
>> + emit_insn (gen_rtx_PARALLEL (VOIDmode, v));
>> +}
>> +
>> /* This routine will either emit the mem_thread_fence pattern or issue a
>> sync_synchronize to generate a fence for memory model MEMMODEL. */
>>
>> diff --git a/gcc/optabs.h b/gcc/optabs.h
>> index 0b14700..bfa10c8 100644
>> --- a/gcc/optabs.h
>> +++ b/gcc/optabs.h
>> @@ -345,6 +345,8 @@ rtx expand_atomic_store (rtx, rtx, enum memmodel, bool);
>> rtx expand_atomic_fetch_op (rtx, rtx, rtx, enum rtx_code, enum memmodel,
>> bool);
>>
>> +extern void expand_asm_reg_clobber_mem_blockage (HARD_REG_SET);
>> +
>> extern bool insn_operand_matches (enum insn_code icode, unsigned int opno,
>> rtx operand);
>> extern bool valid_multiword_target_p (rtx);
>> diff --git a/gcc/opts.c b/gcc/opts.c
>> index 3bda59a..f95a1f0 100644
>> --- a/gcc/opts.c
>> +++ b/gcc/opts.c
>> @@ -1776,6 +1776,24 @@ const struct sanitizer_opts_s
>> coverage_sanitizer_opts[] =
>> { NULL, 0U, 0UL, false }
>> };
>>
>> +/* -fzero-call-used-regs= suboptions. */
>> +const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] =
>> +{
>> +#define ZERO_CALL_USED_REGS_OPT(name, flags) \
>> + { #name, flags }
>> + ZERO_CALL_USED_REGS_OPT (skip, SKIP),
>> + ZERO_CALL_USED_REGS_OPT (used-gpr-arg, (ONLY_USED | ONLY_GPR | ONLY_ARG)),
>> + ZERO_CALL_USED_REGS_OPT (used-arg, (ONLY_USED | ONLY_ARG)),
>> + ZERO_CALL_USED_REGS_OPT (all-gpr-arg, (ONLY_GPR | ONLY_ARG)),
>> + ZERO_CALL_USED_REGS_OPT (all-arg, ONLY_ARG),
>> + ZERO_CALL_USED_REGS_OPT (used-gpr, (ONLY_USED | ONLY_GPR)),
>> + ZERO_CALL_USED_REGS_OPT (all-gpr, ONLY_GPR),
>> + ZERO_CALL_USED_REGS_OPT (used, ONLY_USED),
>> + ZERO_CALL_USED_REGS_OPT (all, ALL),
>> +#undef ZERO_CALL_USED_REGS_OPT
>> + {NULL, 0U}
>> +};
>> +
>> /* A struct for describing a run of chars within a string. */
>>
>> class string_fragment
>> @@ -1970,6 +1988,30 @@ parse_no_sanitize_attribute (char *value)
>> return flags;
>> }
>>
>> +/* Parse -fzero-call-used-regs suboptions from ARG, return the FLAGS. */
>> +
>> +unsigned int
>> +parse_zero_call_used_regs_options (const char *arg)
>> +{
>> + bool found = false;
>> + unsigned int flags = 0;
>> + unsigned int i;
>> +
>> + /* Check to see if the string matches a sub-option name. */
>> + for (i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
>> + if (strcmp (arg, zero_call_used_regs_opts[i].name) == 0)
>> + {
>> + flags |= zero_call_used_regs_opts[i].flag;
>> + found = true;
>
> Same comments as above.
Okay.
>
>> + break;
>> + }
>> +
>> + if (!found)
>> + error ("unrecognized argument to %<-fzero-call-used-regs=%>: %qs", arg);
>
> Think we should use %qs for the option name too, to reduce the number
> of translation strings.
I will try to do this.
>
>> diff --git a/gcc/recog.c b/gcc/recog.c
>> index ce83b7f..e231b5d 100644
>> --- a/gcc/recog.c
>> +++ b/gcc/recog.c
>> @@ -923,6 +923,22 @@ validate_simplify_insn (rtx_insn *insn)
>> return ((num_changes_pending () > 0) && (apply_change_group () > 0));
>> }
>>
>>
>> +
>> +/* Check whether INSN matches a specific alternative of an .md pattern. */
>> +bool
>> +valid_insn_p (rtx_insn *insn)
>
> Very minor nit, but it's unusual to have three blank lines before
> the comment and none afterwards. The codebase isn't very consistent
> about this, but local style seems mostly to be one blank line before
> the comment and one afterwards.
Okay.
>
>> diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c
>> b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c
>> new file mode 100644
>> index 0000000..f44add9
>> --- /dev/null
>> +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c
>> @@ -0,0 +1,15 @@
>> +/* { dg-do run } */
>> +/* { dg-options "-O2 -fzero-call-used-regs=all" } */
>> +
>> +volatile int result = 0;
>> +int
>> +__attribute__((noinline))
>
> “noipa” is stronger. Same for all the tests.
Okay.
>
> The i386 tests are Uros's domain, but I think it would be good to have
> generic tests for all the variants. E.g.:
>
> (1) one test per -fzero-call-used-regs option (including skip)
> (2) one test that tries all valid attribute values (including skip),
> compiled without -fzero-call-used-regs
> (3) one test that #includes (2) but is compiled with an arbitrarily-chosen
> -fzero-call-used-regs (say =all).
> (4) one test that tries invalid uses of the attribute, e.g.:
> - one use of the attribute on a variable
> - one use of the attribute on a function, but with an obviously-wrong
> value
> - one use of the attribute on a function, but with -gpr and -arg the
> wrong way around
You mean to add the above new testing cases to gcc/testsuite/c-c++-common
For all targets?
Then, we cannot test for the assembly matching, we can only testing for “dg-do
run” right?
Thanks.
Qing
>
> (Sorry for not getting to the tests last time.)
>
> Thanks,
> Richard