On Wed, Jun 25, 2025 at 2:14 PM Hongtao Liu <crazy...@gmail.com> wrote: > > On Fri, May 23, 2025 at 1:56 PM H.J. Lu <hjl.to...@gmail.com> wrote: > > > > Add preserve_none attribute which is similar to no_callee_saved_registers > > attribute, except on x86-64, r12, r13, r14, r15, rdi and rsi registers are > > used for integer parameter passing. This can be used in an interpreter > > to avoid saving/restoring the registers in functions which processing > > byte codes. It improved the pystones benchmark by 6-7%: > > > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119628#c15 > > > > Remove -mgeneral-regs-only restriction on no_caller_saved_registers > > attribute. Only SSE is allowed since SSE XMM register load preserves > > the upper bits in YMM/ZMM register while YMM register load zeros the > > upper 256 bits of ZMM register, and preserving 32 ZMM registers can > > be quite expensive. > > > > gcc/ > > > > PR target/119628 > > * config/i386/i386-expand.cc (ix86_expand_call): Call > > ix86_type_no_callee_saved_registers_p instead of looking up > > no_callee_saved_registers attribute. > > * config/i386/i386-options.cc (ix86_set_func_type): Look up > > preserve_none attribute. Check preserve_none attribute for > > interrupt attribute. Don't check no_caller_saved_registers nor > > no_callee_saved_registers conflicts here. > > (ix86_set_func_type): Check no_callee_saved_registers before > > checking no_caller_saved_registers attribute. > > (ix86_set_current_function): Allow SSE with > > no_caller_saved_registers attribute. > > (ix86_handle_call_saved_registers_attribute): Check preserve_none, > > no_callee_saved_registers and no_caller_saved_registers conflicts. > > (ix86_gnu_attributes): Add preserve_none attribute. > > * config/i386/i386-protos.h (ix86_type_no_callee_saved_registers_p): > > New. > > * config/i386/i386.cc > > (x86_64_preserve_none_int_parameter_registers): New. > > (ix86_using_red_zone): Don't use red-zone when there are no > > caller-saved registers with SSE. > > (ix86_type_no_callee_saved_registers_p): New. > > (ix86_function_ok_for_sibcall): Also check TYPE_PRESERVE_NONE > > and call ix86_type_no_callee_saved_registers_p instead of looking > > up no_callee_saved_registers attribute. > > (ix86_comp_type_attributes): Call > > ix86_type_no_callee_saved_registers_p instead of looking up > > no_callee_saved_registers attribute. Return 0 if preserve_none > > attribute doesn't match in 64-bit mode. > > (ix86_function_arg_regno_p): For cfun with TYPE_PRESERVE_NONE, > > use x86_64_preserve_none_int_parameter_registers. > > (init_cumulative_args): Set preserve_none_abi. > > (function_arg_64): Use x86_64_preserve_none_int_parameter_registers > > with preserve_none attribute. > > (setup_incoming_varargs_64): Use > > x86_64_preserve_none_int_parameter_registers with preserve_none > > attribute. > > (ix86_save_reg): Treat TYPE_PRESERVE_NONE like > > TYPE_NO_CALLEE_SAVED_REGISTERS. > > (ix86_nsaved_sseregs): Allow saving XMM registers for > > no_caller_saved_registers attribute. > > (ix86_compute_frame_layout): Likewise. > > (x86_this_parameter): Use > > x86_64_preserve_none_int_parameter_registers with preserve_none > > attribute. > > * config/i386/i386.h (ix86_args): Add preserve_none_abi. > > (call_saved_registers_type): Add TYPE_PRESERVE_NONE. > > (machine_function): Change call_saved_registers to 3 bits. > > * doc/extend.texi: Add preserve_none attribute. Update > > no_caller_saved_registers attribute to remove -mgeneral-regs-only > > restriction. > > > > gcc/testsuite/ > > > > PR target/119628 > > * gcc.target/i386/no-callee-saved-3.c: Adjust error location. > > * gcc.target/i386/no-callee-saved-19a.c: New test. > > * gcc.target/i386/no-callee-saved-19b.c: Likewise. > > * gcc.target/i386/no-callee-saved-19c.c: Likewise. > > * gcc.target/i386/no-callee-saved-19d.c: Likewise. > > * gcc.target/i386/no-callee-saved-19e.c: Likewise. > > * gcc.target/i386/preserve-none-1.c: Likewise. > > * gcc.target/i386/preserve-none-2.c: Likewise. > > * gcc.target/i386/preserve-none-3.c: Likewise. > > * gcc.target/i386/preserve-none-4.c: Likewise. > > * gcc.target/i386/preserve-none-5.c: Likewise. > > * gcc.target/i386/preserve-none-6.c: Likewise. > > * gcc.target/i386/preserve-none-7.c: Likewise. > > * gcc.target/i386/preserve-none-8.c: Likewise. > > * gcc.target/i386/preserve-none-9.c: Likewise. > > * gcc.target/i386/preserve-none-10.c: Likewise. > > * gcc.target/i386/preserve-none-11.c: Likewise. > > * gcc.target/i386/preserve-none-12.c: Likewise. > > * gcc.target/i386/preserve-none-13.c: Likewise. > > * gcc.target/i386/preserve-none-14.c: Likewise. > > * gcc.target/i386/preserve-none-15.c: Likewise. > > * gcc.target/i386/preserve-none-16.c: Likewise. > > * gcc.target/i386/preserve-none-17.c: Likewise. > > * gcc.target/i386/preserve-none-19.c: Likewise. > > * gcc.target/i386/preserve-none-19.c: Likewise. > > * gcc.target/i386/preserve-none-20.c: Likewise. > > * gcc.target/i386/preserve-none-21.c: Likewise. > > * gcc.target/i386/preserve-none-22.c: Likewise. > > * gcc.target/i386/preserve-none-23.c: Likewise. > > * gcc.target/i386/preserve-none-24.c: Likewise. > > * gcc.target/i386/preserve-none-25.c: Likewise. > > * gcc.target/i386/preserve-none-26.c: Likewise. > > * gcc.target/i386/preserve-none-27.c: Likewise. > > * gcc.target/i386/preserve-none-28.c: Likewise. > > * gcc.target/i386/preserve-none-29.c: Likewise. > > * gcc.target/i386/preserve-none-30a.c: Likewise. > > * gcc.target/i386/preserve-none-30b.c: Likewise. > > > > Signed-off-by: H.J. Lu <hjl.to...@gmail.com> > > --- > > gcc/config/i386/i386-expand.cc | 6 +- > > gcc/config/i386/i386-options.cc | 97 +++++++--- > > gcc/config/i386/i386-protos.h | 1 + > > gcc/config/i386/i386.cc | 106 ++++++++--- > > gcc/config/i386/i386.h | 7 +- > > gcc/doc/extend.texi | 14 +- > > .../gcc.target/i386/no-callee-saved-19a.c | 166 ++++++++++++++++++ > > .../gcc.target/i386/no-callee-saved-19b.c | 129 ++++++++++++++ > > .../gcc.target/i386/no-callee-saved-19c.c | 94 ++++++++++ > > .../gcc.target/i386/no-callee-saved-19d.c | 159 +++++++++++++++++ > > .../gcc.target/i386/no-callee-saved-19e.c | 162 +++++++++++++++++ > > .../gcc.target/i386/no-callee-saved-3.c | 4 +- > > .../gcc.target/i386/preserve-none-1.c | 17 ++ > > .../gcc.target/i386/preserve-none-10.c | 11 ++ > > .../gcc.target/i386/preserve-none-11.c | 12 ++ > > .../gcc.target/i386/preserve-none-12.c | 49 ++++++ > > .../gcc.target/i386/preserve-none-13.c | 50 ++++++ > > .../gcc.target/i386/preserve-none-14.c | 49 ++++++ > > .../gcc.target/i386/preserve-none-15.c | 46 +++++ > > .../gcc.target/i386/preserve-none-16.c | 11 ++ > > .../gcc.target/i386/preserve-none-17.c | 10 ++ > > .../gcc.target/i386/preserve-none-18.c | 17 ++ > > .../gcc.target/i386/preserve-none-19.c | 17 ++ > > .../gcc.target/i386/preserve-none-2.c | 12 ++ > > .../gcc.target/i386/preserve-none-20.c | 18 ++ > > .../gcc.target/i386/preserve-none-21.c | 16 ++ > > .../gcc.target/i386/preserve-none-22.c | 17 ++ > > .../gcc.target/i386/preserve-none-23.c | 51 ++++++ > > .../gcc.target/i386/preserve-none-24.c | 8 + > > .../gcc.target/i386/preserve-none-25.c | 27 +++ > > .../gcc.target/i386/preserve-none-26.c | 27 +++ > > .../gcc.target/i386/preserve-none-27.c | 33 ++++ > > .../gcc.target/i386/preserve-none-28.c | 48 +++++ > > .../gcc.target/i386/preserve-none-29.c | 57 ++++++ > > .../gcc.target/i386/preserve-none-3.c | 18 ++ > > .../gcc.target/i386/preserve-none-30a.c | 31 ++++ > > .../gcc.target/i386/preserve-none-30b.c | 21 +++ > > .../gcc.target/i386/preserve-none-4.c | 19 ++ > > .../gcc.target/i386/preserve-none-5.c | 18 ++ > > .../gcc.target/i386/preserve-none-6.c | 30 ++++ > > .../gcc.target/i386/preserve-none-7.c | 30 ++++ > > .../gcc.target/i386/preserve-none-8.c | 8 + > > .../gcc.target/i386/preserve-none-9.c | 8 + > > 43 files changed, 1680 insertions(+), 51 deletions(-) > > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19a.c > > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c > > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19c.c > > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c > > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-1.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-10.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-11.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-12.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-13.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-14.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-15.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-16.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-17.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-18.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-19.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-2.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-20.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-21.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-22.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-23.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-24.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-25.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-26.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-27.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-28.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-29.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-3.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-30a.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-30b.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-4.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-5.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-6.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-7.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-8.c > > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-9.c > > > > diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc > > index 7fd03c88630..f44279848d9 100644 > > --- a/gcc/config/i386/i386-expand.cc > > +++ b/gcc/config/i386/i386-expand.cc > > @@ -10119,8 +10119,7 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx > > callarg1, > > if (lookup_attribute ("interrupt", > > TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) > > error ("interrupt service routine cannot be called directly"); > > - else if (lookup_attribute ("no_callee_saved_registers", > > - TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) > > + else if (ix86_type_no_callee_saved_registers_p (TREE_TYPE > > (fndecl))) > > call_no_callee_saved_registers = true; > > } > > } > > @@ -10131,8 +10130,7 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx > > callarg1, > > tree mem_expr = MEM_EXPR (fnaddr); > > if (mem_expr != nullptr > > && TREE_CODE (mem_expr) == MEM_REF > > - && lookup_attribute ("no_callee_saved_registers", > > - TYPE_ATTRIBUTES (TREE_TYPE (mem_expr)))) > > + && ix86_type_no_callee_saved_registers_p (TREE_TYPE > > (mem_expr))) > > call_no_callee_saved_registers = true; > > } > > > > diff --git a/gcc/config/i386/i386-options.cc > > b/gcc/config/i386/i386-options.cc > > index 12438673503..137cdff3170 100644 > > --- a/gcc/config/i386/i386-options.cc > > +++ b/gcc/config/i386/i386-options.cc > > @@ -3277,7 +3277,10 @@ ix86_set_func_type (tree fndecl) > > interrupt function in this case. */ > > enum call_saved_registers_type no_callee_saved_registers > > = TYPE_DEFAULT_CALL_SAVED_REGISTERS; > > - if (lookup_attribute ("no_callee_saved_registers", > > + if (lookup_attribute ("preserve_none", > > + TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) > > + no_callee_saved_registers = TYPE_PRESERVE_NONE; > > + else if (lookup_attribute ("no_callee_saved_registers", > > TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) > > no_callee_saved_registers = TYPE_NO_CALLEE_SAVED_REGISTERS; > > else if (ix86_noreturn_no_callee_saved_registers > > @@ -3301,9 +3304,16 @@ ix86_set_func_type (tree fndecl) > > "interrupt and naked attributes are not compatible"); > > > > if (no_callee_saved_registers) > > - error_at (DECL_SOURCE_LOCATION (fndecl), > > - "%qs and %qs attributes are not compatible", > > - "interrupt", "no_callee_saved_registers"); > > + { > > + const char *attr; > > + if (no_callee_saved_registers == TYPE_PRESERVE_NONE) > > + attr = "preserve_none"; > > + else > > + attr = "no_callee_saved_registers"; > > + error_at (DECL_SOURCE_LOCATION (fndecl), > > + "%qs and %qs attributes are not compatible", > > + "interrupt", attr); > > + } > > > > int nargs = 0; > > for (tree arg = DECL_ARGUMENTS (fndecl); > > @@ -3325,21 +3335,13 @@ ix86_set_func_type (tree fndecl) > > else > > { > > cfun->machine->func_type = TYPE_NORMAL; > > - if (lookup_attribute ("no_caller_saved_registers", > > - TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) > > + if (no_callee_saved_registers) > > + cfun->machine->call_saved_registers > > + = no_callee_saved_registers; > > + else if (lookup_attribute ("no_caller_saved_registers", > > + TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) > > cfun->machine->call_saved_registers > > = TYPE_NO_CALLER_SAVED_REGISTERS; > > - if (no_callee_saved_registers) > > - { > > - if (cfun->machine->call_saved_registers > > - == TYPE_NO_CALLER_SAVED_REGISTERS) > > - error_at (DECL_SOURCE_LOCATION (fndecl), > > - "%qs and %qs attributes are not compatible", > > - "no_caller_saved_registers", > > - "no_callee_saved_registers"); > > - cfun->machine->call_saved_registers > > - = no_callee_saved_registers; > > - } > > } > > } > > } > > @@ -3528,11 +3530,21 @@ ix86_set_current_function (tree fndecl) > > || (cfun->machine->call_saved_registers > > == TYPE_NO_CALLER_SAVED_REGISTERS)) > > { > > - /* Don't allow SSE, MMX nor x87 instructions since they > > - may change processor state. */ > > + /* Don't allow AVX, AVX512, MMX nor x87 instructions since they > > + may change processor state. Don't allow SSE instructions in > > + exception/interrupt service routines. */ > > const char *isa; > > if (TARGET_SSE) > > - isa = "SSE"; > > + { > > + if (TARGET_AVX512F) > > + isa = "AVX512"; > > + else if (TARGET_AVX) > > + isa = "AVX"; > > + else if (cfun->machine->func_type != TYPE_NORMAL) > > + isa = "SSE"; > > + else > > + isa = NULL; > We can't guarantee there's no mmx/x87 instructions, so need to add > !TARGET_MMX && !TARGET_80387?
For functions with no_callee_saved_registers attribute, MMX and x87 registers are special: bool ix86_epilogue_uses (int regno) { /* If there are no caller-saved registers, we preserve all registers, except for MMX and x87 registers which aren't supported when saving and restoring registers. Don't explicitly save SP register since it is always preserved. */ return (epilogue_completed && (cfun->machine->call_saved_registers == TYPE_NO_CALLER_SAVED_REGISTERS) && !fixed_regs[regno] && !STACK_REGNO_P (regno) && !MMX_REGNO_P (regno)); } ... /* Return TRUE if we need to save REGNO. */ bool ix86_save_reg (unsigned int regno, bool maybe_eh_return, bool ignore_outlined) { rtx reg; switch (cfun->machine->call_saved_registers) { case TYPE_DEFAULT_CALL_SAVED_REGISTERS: break; case TYPE_NO_CALLER_SAVED_REGISTERS: /* If there are no caller-saved registers, we preserve all registers, except for MMX and x87 registers which aren't supported when saving and restoring registers. Don't explicitly save SP register since it is always preserved. Don't preserve registers used for function return value. */ There are no need to check !TARGET_MMX && !TARGET_80387?. > Perhaps rewrite the conditions with > if (TARGET_AVX512F) > isa = "AVX512"; > else if (TARGET_AVX) > isa = "AVX"; > else if (TARGET_MMX) > isa = "MMX/3Dnow"; > else if (TARGET_80387) > isa = "80387"; > else if (TARGET_SSE) > isa = cfun->machine->func_type != TYPE_NORMAL ? "SSE" : NULL; > else > isa = NULL; > > > + } > > else if (TARGET_MMX) > > isa = "MMX/3Dnow"; > > else if (TARGET_80387) > > @@ -3957,9 +3969,50 @@ ix86_handle_fndecl_attribute (tree *node, tree name, > > tree args, int, > > } > > > > static tree > > -ix86_handle_call_saved_registers_attribute (tree *, tree, tree, > > +ix86_handle_call_saved_registers_attribute (tree *node, tree name, tree, > > int, bool *) > > { > > + const char *attr1 = nullptr; > > + const char *attr2 = nullptr; > > + > > + if (is_attribute_p ("no_callee_saved_registers", name)) > > + { > > + /* Disallow preserve_none and no_caller_saved_registers > > + attributes. */ > > + attr1 = "no_callee_saved_registers"; > > + if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (*node))) > > + attr2 = "preserve_none"; > > + else if (lookup_attribute ("no_caller_saved_registers", > > + TYPE_ATTRIBUTES (*node))) > > + attr2 = "no_caller_saved_registers"; > > + } > > + else if (is_attribute_p ("no_caller_saved_registers", name)) > > + { > > + /* Disallow preserve_none and no_callee_saved_registers > > + attributes. */ > > + attr1 = "no_caller_saved_registers"; > > + if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (*node))) > > + attr2 = "preserve_none"; > > + else if (lookup_attribute ("no_callee_saved_registers", > > + TYPE_ATTRIBUTES (*node))) > > + attr2 = "no_callee_saved_registers"; > > + } > > + else if (is_attribute_p ("preserve_none", name)) > > + { > > + /* Disallow no_callee_saved_registers and no_caller_saved_registers > > + attributes. */ > > + attr1 = "preserve_none"; > > + if (lookup_attribute ("no_callee_saved_registers", > > + TYPE_ATTRIBUTES (*node))) > > + attr2 = "no_caller_saved_registers"; > > + else if (lookup_attribute ("no_callee_saved_registers", > > + TYPE_ATTRIBUTES (*node))) > > + attr2 = "no_callee_saved_registers"; > > + } > > + > > + if (attr2) > > + error ("%qs and %qs attributes are not compatible", attr1, attr2); > > + > > return NULL_TREE; > > } > > > > @@ -4121,6 +4174,8 @@ static const attribute_spec ix86_gnu_attributes[] = > > ix86_handle_interrupt_attribute, NULL }, > > { "no_caller_saved_registers", 0, 0, false, true, true, false, > > ix86_handle_call_saved_registers_attribute, NULL }, > > + { "preserve_none", 0, 0, false, true, true, true, > > + ix86_handle_call_saved_registers_attribute, NULL }, > > { "no_callee_saved_registers", 0, 0, false, true, true, true, > > ix86_handle_call_saved_registers_attribute, NULL }, > > { "naked", 0, 0, true, false, false, false, > > diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h > > index e85b925704b..263852ceead 100644 > > --- a/gcc/config/i386/i386-protos.h > > +++ b/gcc/config/i386/i386-protos.h > > @@ -281,6 +281,7 @@ extern tree ix86_valid_target_attribute_tree (tree, > > tree, > > struct gcc_options *, > > struct gcc_options *, bool); > > extern unsigned int ix86_get_callcvt (const_tree); > > +extern bool ix86_type_no_callee_saved_registers_p (const_tree); > > > > #endif > > > > diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc > > index 5cb66dadb43..dc5214a4774 100644 > > --- a/gcc/config/i386/i386.cc > > +++ b/gcc/config/i386/i386.cc > > @@ -335,6 +335,14 @@ static int const > > x86_64_ms_abi_int_parameter_registers[4] = > > CX_REG, DX_REG, R8_REG, R9_REG > > }; > > > > +/* The same as Clang's preserve_none function parameter passing. > It's not exactly the same as Clang's preserve_none, So perhaps use > Similar as Clang's .... Fixed. Will send the v3 patch. Thanks. -- H.J.