On Thu, Sep 27, 2018 at 7:58 AM Richard Biener <richard.guent...@gmail.com> wrote: > > On Thu, Sep 27, 2018 at 3:16 PM H.J. Lu <hjl.to...@gmail.com> wrote: > > > > On Thu, Sep 27, 2018 at 6:08 AM, Szabolcs Nagy <szabolcs.n...@arm.com> > > wrote: > > > On 26/09/18 19:10, H.J. Lu wrote: > > >> > > >> Add -mzero-caller-saved-regs=[skip|used|all] command-line option and > > >> zero_caller_saved_regs("skip|used|all") function attribue: > > >> > > >> 1. -mzero-caller-saved-regs=skip and zero_caller_saved_regs("skip") > > >> > > >> Don't zero caller-saved integer registers upon function return. > > >> > > >> 2. -mzero-caller-saved-regs=used and zero_caller_saved_regs("used") > > >> > > >> Zero used caller-saved integer registers upon function return. > > >> > > >> 3. -mzero-caller-saved-regs=all and zero_caller_saved_regs("all") > > >> > > >> Zero all caller-saved integer registers upon function return. > > >> > > >> Tested on i686 and x86-64 with bootstrapping GCC trunk and > > >> -mzero-caller-saved-regs=used as well as -mzero-caller-saved-regs=all > > >> enabled by default. > > >> > > > > > > from this description and the documentation it's > > > not clear to me what this tries to achieve. > > > > > > is it trying to prevent information leak? > > > or some pcs hack the caller may rely on? > > > > > > if it's for information leak then i'd expect such > > > attribute to be used on crypto code.. however i'd > > > expect crypto code to use simd registers as well, > > > so integer only cleaning needs explanation. > > > > The target usage is in Linux kernel. > > Maybe still somehow encode that in the option since it otherwise raises > expectations that are not met? > -mzero-call-clobbered-regs=used-int|all-int|skip|used-simd|used-fp,etc.? > and sorry() on unimplemented ones? Or simply zero also non-integer > regs the same way? I suppose > there isn't sth like vzeroupper that zeros all SIMD regs and completely? >
Here is the updated patch to zero caller-saved vector registers. I don't mind a different option name if it is preferred. I may be able to create some generic utility functions which can be used by other backends. But actual implementation must be target specific. Any comments? Thanks. -- H.J.
From a96829155f6df6d4a276b3ef07f06cb7c5d3cb82 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.tools@gmail.com> Date: Tue, 6 Feb 2018 13:31:15 -0800 Subject: [PATCH] x86: Add -mzero-caller-saved-regs=[skip|used-gpr|all-gpr|used|all] Add -mzero-caller-saved-regs=[skip|used-gpr|all-gpr|used|all] command-line option and zero_caller_saved_regs("skip|used|all") function attribue: 1. -mzero-caller-saved-regs=skip and zero_caller_saved_regs("skip") Don't zero caller-saved registers upon function return. 2. -mzero-caller-saved-regs=used-gpr and zero_caller_saved_regs("used-gpr") Zero used caller-saved integer registers upon function return. 3. -mzero-caller-saved-regs=all-gpr and zero_caller_saved_regs("all-gpr") 2. -mzero-caller-saved-regs=used and zero_caller_saved_regs("used") Zero used caller-saved integer and vector registers upon function return. 3. -mzero-caller-saved-regs=all and zero_caller_saved_regs("all") Zero all caller-saved integer and vector registers upon function return. Tested on i686 and x86-64 with bootstrapping GCC trunk, making -mzero-caller-saved-regs=used-gpr, -mzero-caller-saved-regs=all-gpr -mzero-caller-saved-regs=used, and -mzero-caller-saved-regs=all enabled by default. gcc/ * config/i386/i386-opts.h (zero_caller_saved_regs): New enum. * config/i386/i386-protos.h (ix86_split_simple_return_pop_internal): Renamed to ... (ix86_split_simple_return_internal): This. * config/i386/i386.c (ix86_set_zero_caller_saved_regs_type): New function. (ix86_set_current_function): Call ix86_set_zero_caller_saved_regs_type. (ix86_expand_prologue): Replace gen_prologue_use with gen_pro_epilogue_use. (ix86_expand_epilogue): Replace gen_simple_return_pop_internal with ix86_split_simple_return_internal. Replace gen_simple_return_internal with ix86_split_simple_return_internal. (ix86_find_live_outgoing_regs): New function. (ix86_split_simple_return_pop_internal): Removed. (ix86_split_simple_return_internal): New function. (ix86_handle_fndecl_attribute): Support zero_caller_saved_regs attribute. (ix86_attribute_table): Add zero_caller_saved_regs. * config/i386/i386.h (machine_function): Add zero_caller_saved_regs_type, live_outgoing_int_regs and live_outgoing_vector_regs. (TARGET_POP_SCRATCH_REGISTER): New. * config/i386/i386.md (UNSPEC_SIMPLE_RETURN): New UNSPEC. (UNSPECV_PROLOGUE_USE): Renamed to ... (UNSPECV_PRO_EPILOGUE_USE): This. (prologue_use): Renamed to ... (pro_epilogue_use): This. (simple_return_internal): Changed to define_insn_and_split. (simple_return_internal_1): New pattern. (simple_return_pop_internal): Replace ix86_split_simple_return_pop_internal with ix86_split_simple_return_internal. Always call ix86_split_simple_return_internal if epilogue_completed is true. (simple_return_pop_internal_1): New pattern. (Epilogue deallocator to pop peepholes): Enabled only if TARGET_POP_SCRATCH_REGISTER is true. * config/i386/i386.opt (mzero-caller-saved-regs=): New option. * doc/extend.texi: Document zero_caller_saved_regs attribute. * doc/invoke.texi: Document -mzero-caller-saved-regs=. gcc/testsuite/ * gcc.target/i386/zero-scratch-regs-1.c: New test. * gcc.target/i386/zero-scratch-regs-2.c: Likewise. * gcc.target/i386/zero-scratch-regs-3.c: Likewise. * gcc.target/i386/zero-scratch-regs-4.c: Likewise. * gcc.target/i386/zero-scratch-regs-5.c: Likewise. * gcc.target/i386/zero-scratch-regs-6.c: Likewise. * gcc.target/i386/zero-scratch-regs-7.c: Likewise. * gcc.target/i386/zero-scratch-regs-8.c: Likewise. * gcc.target/i386/zero-scratch-regs-9.c: Likewise. * gcc.target/i386/zero-scratch-regs-10.c: Likewise. * gcc.target/i386/zero-scratch-regs-11.c: Likewise. * gcc.target/i386/zero-scratch-regs-12.c: Likewise. * gcc.target/i386/zero-scratch-regs-13.c: Likewise. * gcc.target/i386/zero-scratch-regs-14.c: Likewise. * gcc.target/i386/zero-scratch-regs-15.c: Likewise. * gcc.target/i386/zero-scratch-regs-16.c: Likewise. * gcc.target/i386/zero-scratch-regs-17.c: Likewise. * gcc.target/i386/zero-scratch-regs-18.c: Likewise. * gcc.target/i386/zero-scratch-regs-19.c: Likewise. * gcc.target/i386/zero-scratch-regs-20.c: Likewise. * gcc.target/i386/zero-scratch-regs-21.c: Likewise. * gcc.target/i386/zero-scratch-regs-22.c: Likewise. * gcc.target/i386/zero-scratch-regs-23.c: Likewise. --- gcc/config/i386/i386-opts.h | 9 + gcc/config/i386/i386-protos.h | 2 +- gcc/config/i386/i386.c | 356 ++++++++++++++++-- gcc/config/i386/i386.h | 16 + gcc/config/i386/i386.md | 54 ++- gcc/config/i386/i386.opt | 23 ++ gcc/doc/extend.texi | 12 + gcc/doc/invoke.texi | 14 +- .../gcc.target/i386/zero-scratch-regs-1.c | 12 + .../gcc.target/i386/zero-scratch-regs-10.c | 21 ++ .../gcc.target/i386/zero-scratch-regs-11.c | 39 ++ .../gcc.target/i386/zero-scratch-regs-12.c | 39 ++ .../gcc.target/i386/zero-scratch-regs-13.c | 21 ++ .../gcc.target/i386/zero-scratch-regs-14.c | 19 + .../gcc.target/i386/zero-scratch-regs-15.c | 14 + .../gcc.target/i386/zero-scratch-regs-16.c | 14 + .../gcc.target/i386/zero-scratch-regs-17.c | 13 + .../gcc.target/i386/zero-scratch-regs-18.c | 13 + .../gcc.target/i386/zero-scratch-regs-19.c | 12 + .../gcc.target/i386/zero-scratch-regs-2.c | 19 + .../gcc.target/i386/zero-scratch-regs-20.c | 23 ++ .../gcc.target/i386/zero-scratch-regs-21.c | 14 + .../gcc.target/i386/zero-scratch-regs-22.c | 19 + .../gcc.target/i386/zero-scratch-regs-23.c | 19 + .../gcc.target/i386/zero-scratch-regs-3.c | 12 + .../gcc.target/i386/zero-scratch-regs-4.c | 14 + .../gcc.target/i386/zero-scratch-regs-5.c | 20 + .../gcc.target/i386/zero-scratch-regs-6.c | 14 + .../gcc.target/i386/zero-scratch-regs-7.c | 13 + .../gcc.target/i386/zero-scratch-regs-8.c | 19 + .../gcc.target/i386/zero-scratch-regs-9.c | 15 + 31 files changed, 867 insertions(+), 37 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h index 46366cbfa72..65750ebfc6b 100644 --- a/gcc/config/i386/i386-opts.h +++ b/gcc/config/i386/i386-opts.h @@ -119,4 +119,13 @@ enum indirect_branch { indirect_branch_thunk_extern }; +enum zero_caller_saved_regs { + zero_caller_saved_regs_unset = 0, + zero_caller_saved_regs_skip, + zero_caller_saved_regs_used_gpr, + zero_caller_saved_regs_all_gpr, + zero_caller_saved_regs_used, + zero_caller_saved_regs_all +}; + #endif diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 258bdd18cb5..30bc2835f3f 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -313,7 +313,7 @@ extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op); extern const char * ix86_output_indirect_jmp (rtx call_op); extern const char * ix86_output_function_return (bool long_p); extern const char * ix86_output_indirect_function_return (rtx ret_op); -extern void ix86_split_simple_return_pop_internal (rtx); +extern void ix86_split_simple_return_internal (rtx); extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load, machine_mode mode); extern int ix86_min_insn_size (rtx_insn *); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 963c7fcbb34..692680e9caa 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -5559,6 +5559,46 @@ ix86_set_func_type (tree fndecl) } } +/* Set the zero_caller_saved_regs_type field from the function FNDECL. */ + +static void +ix86_set_zero_caller_saved_regs_type (tree fndecl) +{ + if (cfun->machine->zero_caller_saved_regs_type + == zero_caller_saved_regs_unset) + { + tree attr = lookup_attribute ("zero_caller_saved_regs", + DECL_ATTRIBUTES (fndecl)); + if (attr != NULL) + { + tree args = TREE_VALUE (attr); + if (args == NULL) + gcc_unreachable (); + tree cst = TREE_VALUE (args); + if (strcmp (TREE_STRING_POINTER (cst), "skip") == 0) + cfun->machine->zero_caller_saved_regs_type + = zero_caller_saved_regs_skip; + else if (strcmp (TREE_STRING_POINTER (cst), "used-gpr") == 0) + cfun->machine->zero_caller_saved_regs_type + = zero_caller_saved_regs_used_gpr; + else if (strcmp (TREE_STRING_POINTER (cst), "all-gpr") == 0) + cfun->machine->zero_caller_saved_regs_type + = zero_caller_saved_regs_all_gpr; + else if (strcmp (TREE_STRING_POINTER (cst), "used") == 0) + cfun->machine->zero_caller_saved_regs_type + = zero_caller_saved_regs_used; + else if (strcmp (TREE_STRING_POINTER (cst), "all") == 0) + cfun->machine->zero_caller_saved_regs_type + = zero_caller_saved_regs_all; + else + gcc_unreachable (); + } + else + cfun->machine->zero_caller_saved_regs_type + = ix86_zero_caller_saved_regs; + } +} + /* Set the indirect_branch_type field from the function FNDECL. */ static void @@ -5659,6 +5699,7 @@ ix86_set_current_function (tree fndecl) { ix86_set_func_type (fndecl); ix86_set_indirect_branch_type (fndecl); + ix86_set_zero_caller_saved_regs_type (fndecl); } return; } @@ -5680,6 +5721,7 @@ ix86_set_current_function (tree fndecl) ix86_set_func_type (fndecl); ix86_set_indirect_branch_type (fndecl); + ix86_set_zero_caller_saved_regs_type (fndecl); tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl); if (new_tree == NULL_TREE) @@ -13553,7 +13595,7 @@ ix86_expand_prologue (void) insn = emit_insn (gen_set_got (pic)); RTX_FRAME_RELATED_P (insn) = 1; add_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL_RTX); - emit_insn (gen_prologue_use (pic)); + emit_insn (gen_pro_epilogue_use (pic)); /* Deleting already emmitted SET_GOT if exist and allocated to REAL_PIC_OFFSET_TABLE_REGNUM. */ ix86_elim_entry_set_got (pic); @@ -13582,7 +13624,7 @@ ix86_expand_prologue (void) Further, prevent alloca modifications to the stack pointer from being combined with prologue modifications. */ if (TARGET_SEH) - emit_insn (gen_prologue_use (stack_pointer_rtx)); + emit_insn (gen_pro_epilogue_use (stack_pointer_rtx)); } /* Emit code to restore REG using a POP insn. */ @@ -14300,7 +14342,7 @@ ix86_expand_epilogue (int style) emit_jump_insn (gen_simple_return_indirect_internal (ecx)); } else - emit_jump_insn (gen_simple_return_pop_internal (popc)); + ix86_split_simple_return_internal (popc); } else if (!m->call_ms2sysv || !restore_stub_is_tail) { @@ -14327,7 +14369,7 @@ ix86_expand_epilogue (int style) emit_jump_insn (gen_simple_return_indirect_internal (ecx)); } else - emit_jump_insn (gen_simple_return_internal ()); + ix86_split_simple_return_internal (NULL_RTX); } /* Restore the state back to the state from the prologue, @@ -28404,37 +28446,272 @@ ix86_output_indirect_function_return (rtx ret_op) return "%!jmp\t%A0"; } -/* Split simple return with popping POPC bytes from stack to indirect - branch with stack adjustment . */ +/* Find general registers which are live at the exit of basic block BB + and set their corresponding bits in LIVE_OUTGOING_REGS. */ + +static void +ix86_find_live_outgoing_regs (basic_block bb, bool gpr, bool zero_all, + unsigned int &live_outgoing_regs) +{ + bitmap live_out = df_get_live_out (bb); + + unsigned int regno; + + /* Check for live outgoing registers. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + unsigned int i = INVALID_REGNUM; + + if (gpr) + { + /* Zero general registers. */ + if (LEGACY_INT_REGNO_P (regno)) + i = regno; + else if (TARGET_64BIT && REX_INT_REGNO_P (regno)) + i = regno - FIRST_REX_INT_REG + 8; + } + else if (TARGET_SSE) + { + /* Zero vector registers. */ + if (IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG)) + i = regno - FIRST_SSE_REG; + else if (TARGET_64BIT) + { + if (REX_SSE_REGNO_P (regno)) + i = regno - FIRST_REX_SSE_REG + 8; + else if (TARGET_AVX512F && EXT_REX_SSE_REGNO_P (regno)) + i = regno - FIRST_EXT_REX_SSE_REG + 16; + } + } + + if (i == INVALID_REGNUM) + continue; + + /* No need to check it again if it is live. */ + if ((live_outgoing_regs & (1 << i))) + continue; + + /* A register is considered LIVE if + 1. It is a fixed register. + 2. If isn't a caller-saved register. + 3. If it is a live outgoing register. + 4. It is never used in the function and we don't zero all + caller-saved registers. + */ + if (fixed_regs[regno] + || !call_used_regs[regno] + || REGNO_REG_SET_P (live_out, regno) + || (!zero_all && !df_regs_ever_live_p (regno))) + live_outgoing_regs |= 1 << i; + } +} + +/* Split simple return with popping POPC bytes from stack, if POPC + isn't NULL_RTX, and zero caller-saved general registers if needed. + When popping POPC bytes from stack for -mfunction-return=, convert + return to indirect branch with stack adjustment. */ void -ix86_split_simple_return_pop_internal (rtx popc) +ix86_split_simple_return_internal (rtx popc) { - struct machine_function *m = cfun->machine; - rtx ecx = gen_rtx_REG (SImode, CX_REG); - rtx_insn *insn; + /* No need to zero caller-saved registers in main (). Don't zero + caller-saved registers if __builtin_eh_return is called since it + isn't a normal function return. */ + if ((cfun->machine->zero_caller_saved_regs_type + != zero_caller_saved_regs_skip) + && !crtl->calls_eh_return + && cfun->machine->func_type == TYPE_NORMAL + && !MAIN_NAME_P (DECL_NAME (current_function_decl))) + { + bool gpr_only = true; + bool zero_all = false; + switch (cfun->machine->zero_caller_saved_regs_type) + { + case zero_caller_saved_regs_all_gpr: + zero_all = true; + break; + case zero_caller_saved_regs_used: + gpr_only = false; + break; + case zero_caller_saved_regs_all: + gpr_only = false; + zero_all = true; + break; + default: + break; + } - /* There is no "pascal" calling convention in any 64bit ABI. */ - gcc_assert (!TARGET_64BIT); + unsigned int &live_outgoing_int_regs + = cfun->machine->live_outgoing_int_regs; + unsigned int &live_outgoing_vector_regs + = cfun->machine->live_outgoing_vector_regs; - insn = emit_insn (gen_pop (ecx)); - m->fs.cfa_offset -= UNITS_PER_WORD; - m->fs.sp_offset -= UNITS_PER_WORD; + edge e; + edge_iterator ei; - rtx x = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD); - x = gen_rtx_SET (stack_pointer_rtx, x); - add_reg_note (insn, REG_CFA_ADJUST_CFA, x); - add_reg_note (insn, REG_CFA_REGISTER, gen_rtx_SET (ecx, pc_rtx)); - RTX_FRAME_RELATED_P (insn) = 1; + if (live_outgoing_int_regs == 0) + { + /* ECX register is used for return with pop. */ + if (popc != NULL_RTX + && (cfun->machine->function_return_type + != indirect_branch_keep)) + live_outgoing_int_regs = 1 << CX_REG; - x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, popc); - x = gen_rtx_SET (stack_pointer_rtx, x); - insn = emit_insn (x); - add_reg_note (insn, REG_CFA_ADJUST_CFA, x); - RTX_FRAME_RELATED_P (insn) = 1; + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + { + ix86_find_live_outgoing_regs (e->src, true, zero_all, + live_outgoing_int_regs); + } + } + + if (!gpr_only && live_outgoing_vector_regs == 0) + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + { + ix86_find_live_outgoing_regs (e->src, false, zero_all, + live_outgoing_vector_regs); + } + + if (!gpr_only && TARGET_AVX && live_outgoing_vector_regs == 0) + { + emit_insn (gen_avx_vzeroall ()); + gpr_only = true; + } - /* Now return address is in ECX. */ - emit_jump_insn (gen_simple_return_indirect_internal (ecx)); + rtx zero_gpr = NULL_RTX; + rtx zero_vector = NULL_RTX; + + unsigned int regno; + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + unsigned int i = INVALID_REGNUM; + unsigned int live_outgoing_regs; + bool gpr = false; + + if (LEGACY_INT_REGNO_P (regno)) + { + gpr = true; + i = regno; + live_outgoing_regs = live_outgoing_int_regs; + } + else if (TARGET_64BIT && REX_INT_REGNO_P (regno)) + { + gpr = true; + live_outgoing_regs = live_outgoing_int_regs; + i = regno - FIRST_REX_INT_REG + 8; + } + else if (!gpr_only && TARGET_SSE) + { + if (IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG)) + { + live_outgoing_regs = live_outgoing_vector_regs; + i = regno - FIRST_SSE_REG; + } + if (TARGET_64BIT) + { + if (REX_SSE_REGNO_P (regno)) + { + live_outgoing_regs = live_outgoing_vector_regs; + i = regno - FIRST_REX_SSE_REG + 8; + } + else if (TARGET_AVX512F + && EXT_REX_SSE_REGNO_P (regno)) + { + live_outgoing_regs = live_outgoing_vector_regs; + i = regno - FIRST_EXT_REX_SSE_REG + 16; + } + } + } + + if (i == INVALID_REGNUM) + continue; + + if ((live_outgoing_regs & (1 << i))) + continue; + + rtx reg, tmp; + + if (gpr) + { + /* Zero out dead caller-saved register. We only need to + zero the lower 32 bits. */ + reg = gen_rtx_REG (SImode, regno); + if (zero_gpr == NULL_RTX) + { + zero_gpr = reg; + tmp = gen_rtx_SET (reg, const0_rtx); + if (!TARGET_USE_MOV0 || optimize_insn_for_size_p ()) + { + rtx clob = gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (CCmode, + FLAGS_REG)); + tmp = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, + tmp, + clob)); + } + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_gpr); + } + else + { + reg = gen_rtx_REG (V4SFmode, regno); + if (zero_vector == NULL_RTX) + { + zero_vector = reg; + tmp = gen_rtx_SET (reg, const0_rtx); + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_vector); + } + + /* Mark it in use */ + emit_insn (gen_pro_epilogue_use (reg)); + } + } + + if (popc) + { + if (cfun->machine->function_return_type != indirect_branch_keep) + { + struct machine_function *m = cfun->machine; + rtx ecx = gen_rtx_REG (SImode, CX_REG); + rtx_insn *insn; + + /* There is no "pascal" calling convention in any 64bit ABI. */ + gcc_assert (!TARGET_64BIT); + + insn = emit_insn (gen_pop (ecx)); + m->fs.cfa_offset -= UNITS_PER_WORD; + m->fs.sp_offset -= UNITS_PER_WORD; + + rtx x = plus_constant (Pmode, stack_pointer_rtx, + UNITS_PER_WORD); + x = gen_rtx_SET (stack_pointer_rtx, x); + add_reg_note (insn, REG_CFA_ADJUST_CFA, x); + add_reg_note (insn, REG_CFA_REGISTER, + gen_rtx_SET (ecx, pc_rtx)); + RTX_FRAME_RELATED_P (insn) = 1; + + x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, popc); + x = gen_rtx_SET (stack_pointer_rtx, x); + insn = emit_insn (x); + add_reg_note (insn, REG_CFA_ADJUST_CFA, copy_rtx (x)); + RTX_FRAME_RELATED_P (insn) = 1; + + /* Mark ECX in use */ + emit_insn (gen_pro_epilogue_use (ecx)); + + /* Now return address is in ECX. */ + emit_jump_insn (gen_simple_return_indirect_internal (ecx)); + } + else + emit_jump_insn (gen_simple_return_pop_internal_1 (popc)); + } + else + emit_jump_insn (gen_simple_return_internal_1 ()); } /* Output the assembly for a call instruction. */ @@ -40787,6 +41064,29 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int, } } + if (is_attribute_p ("zero_caller_saved_regs", name)) + { + tree cst = TREE_VALUE (args); + if (TREE_CODE (cst) != STRING_CST) + { + warning (OPT_Wattributes, + "%qE attribute requires a string constant argument", + name); + *no_add_attrs = true; + } + else if (strcmp (TREE_STRING_POINTER (cst), "skip") != 0 + && strcmp (TREE_STRING_POINTER (cst), "used-gpr") != 0 + && strcmp (TREE_STRING_POINTER (cst), "all-gpr") != 0 + && strcmp (TREE_STRING_POINTER (cst), "used") != 0 + && strcmp (TREE_STRING_POINTER (cst), "all") != 0) + { + warning (OPT_Wattributes, + "argument to %qE attribute is not (skip|used-gpr|all-gpr|used|all)", + name); + *no_add_attrs = true; + } + } + return NULL_TREE; } @@ -45075,6 +45375,8 @@ static const struct attribute_spec ix86_attribute_table[] = ix86_handle_fndecl_attribute, NULL }, { "indirect_return", 0, 0, false, true, true, false, NULL, NULL }, + { "zero_caller_saved_regs", 1, 1, true, false, false, false, + ix86_handle_fndecl_attribute, NULL }, /* End element. */ { NULL, 0, 0, false, false, false, false, NULL, NULL } diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 01d49a7263b..38022a3751a 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -2720,6 +2720,10 @@ struct GTY(()) machine_function { the "interrupt" or "no_caller_saved_registers" attribute. */ BOOL_BITFIELD no_caller_saved_registers : 1; + /* How to clear caller-saved general registers upon function + return. */ + ENUM_BITFIELD(zero_caller_saved_regs) zero_caller_saved_regs_type : 5; + /* If true, there is register available for argument passing. This is used only in ix86_function_ok_for_sibcall by 32-bit to determine if there is scratch register available for indirect sibcall. In @@ -2747,6 +2751,12 @@ struct GTY(()) machine_function { /* If true, ENDBR is queued at function entrance. */ BOOL_BITFIELD endbr_queued_at_entrance : 1; + /* Integer registers live at exit. */ + unsigned int live_outgoing_int_regs; + + /* Vector registers live at exit. */ + unsigned int live_outgoing_vector_regs; + /* The largest alignment, in bytes, of stack slot actually used. */ unsigned int max_used_stack_alignment; @@ -2846,6 +2856,12 @@ extern void debug_dispatch_window (int); (ix86_indirect_branch_register \ || cfun->machine->indirect_branch_type != indirect_branch_keep) +#define TARGET_POP_SCRATCH_REGISTER \ + (TARGET_64BIT \ + || (cfun->machine->zero_caller_saved_regs_type \ + == zero_caller_saved_regs_skip) \ + || cfun->machine->function_return_type == indirect_branch_keep) + #define IX86_HLE_ACQUIRE (1 << 16) #define IX86_HLE_RELEASE (1 << 17) diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 7fb2b144f47..ffb18a425e1 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -182,6 +182,8 @@ UNSPEC_PDEP UNSPEC_PEXT + UNSPEC_SIMPLE_RETURN + ;; IRET support UNSPEC_INTERRUPT_RETURN ]) @@ -192,7 +194,7 @@ UNSPECV_STACK_PROBE UNSPECV_PROBE_STACK_RANGE UNSPECV_ALIGN - UNSPECV_PROLOGUE_USE + UNSPECV_PRO_EPILOGUE_USE UNSPECV_SPLIT_STACK_RETURN UNSPECV_CLD UNSPECV_NOPS @@ -12828,8 +12830,8 @@ ;; As USE insns aren't meaningful after reload, this is used instead ;; to prevent deleting instructions setting registers for PIC code -(define_insn "prologue_use" - [(unspec_volatile [(match_operand 0)] UNSPECV_PROLOGUE_USE)] +(define_insn "pro_epilogue_use" + [(unspec_volatile [(match_operand 0)] UNSPECV_PRO_EPILOGUE_USE)] "" "" [(set_attr "length" "0")]) @@ -12870,10 +12872,23 @@ } }) -(define_insn "simple_return_internal" +(define_insn_and_split "simple_return_internal" [(simple_return)] "reload_completed" "* return ix86_output_function_return (false);" + "&& epilogue_completed" + [(const_int 0)] + "ix86_split_simple_return_internal (NULL_RTX); DONE;" + [(set_attr "length" "1") + (set_attr "atom_unit" "jeu") + (set_attr "length_immediate" "0") + (set_attr "modrm" "0")]) + +(define_insn "simple_return_internal_1" + [(simple_return) + (unspec [(const_int 0)] UNSPEC_SIMPLE_RETURN)] + "reload_completed" + "* return ix86_output_function_return (false);" [(set_attr "length" "1") (set_attr "atom_unit" "jeu") (set_attr "length_immediate" "0") @@ -12906,9 +12921,21 @@ (use (match_operand:SI 0 "const_int_operand"))] "reload_completed" "%!ret\t%0" - "&& cfun->machine->function_return_type != indirect_branch_keep" + "&& (epilogue_completed + || cfun->machine->function_return_type != indirect_branch_keep)" [(const_int 0)] - "ix86_split_simple_return_pop_internal (operands[0]); DONE;" + "ix86_split_simple_return_internal (operands[0]); DONE;" + [(set_attr "length" "3") + (set_attr "atom_unit" "jeu") + (set_attr "length_immediate" "2") + (set_attr "modrm" "0")]) + +(define_insn "simple_return_pop_internal_1" + [(simple_return) + (use (match_operand:SI 0 "const_int_operand")) + (unspec [(const_int 0)] UNSPEC_SIMPLE_RETURN)] + "reload_completed" + "%!ret\t%0" [(set_attr "length" "3") (set_attr "atom_unit" "jeu") (set_attr "length_immediate" "2") @@ -18626,6 +18653,11 @@ (set (mem:W (pre_dec:P (reg:P SP_REG))) (match_dup 1))]) ;; Convert epilogue deallocator to pop. +;; Don't do it when +;; -mfunction-return= -mzero-caller-saved-regs= +;; is used in 32-bit snce return with stack pop needs to increment +;; stack register and scratch registers must be zeroed. Pop scratch +;; register will load value from stack. (define_peephole2 [(match_scratch:W 1 "r") (parallel [(set (reg:P SP_REG) @@ -18634,6 +18666,7 @@ (clobber (reg:CC FLAGS_REG)) (clobber (mem:BLK (scratch)))])] "(TARGET_SINGLE_POP || optimize_insn_for_size_p ()) + && TARGET_POP_SCRATCH_REGISTER && INTVAL (operands[0]) == GET_MODE_SIZE (word_mode)" [(parallel [(set (match_dup 1) (mem:W (post_inc:P (reg:P SP_REG)))) (clobber (mem:BLK (scratch)))])]) @@ -18649,6 +18682,7 @@ (clobber (reg:CC FLAGS_REG)) (clobber (mem:BLK (scratch)))])] "(TARGET_DOUBLE_POP || optimize_insn_for_size_p ()) + && TARGET_POP_SCRATCH_REGISTER && INTVAL (operands[0]) == 2*GET_MODE_SIZE (word_mode)" [(parallel [(set (match_dup 1) (mem:W (post_inc:P (reg:P SP_REG)))) (clobber (mem:BLK (scratch)))]) @@ -18662,6 +18696,7 @@ (clobber (reg:CC FLAGS_REG)) (clobber (mem:BLK (scratch)))])] "optimize_insn_for_size_p () + && TARGET_POP_SCRATCH_REGISTER && INTVAL (operands[0]) == 2*GET_MODE_SIZE (word_mode)" [(parallel [(set (match_dup 1) (mem:W (post_inc:P (reg:P SP_REG)))) (clobber (mem:BLK (scratch)))]) @@ -18674,7 +18709,8 @@ (plus:P (reg:P SP_REG) (match_operand:P 0 "const_int_operand"))) (clobber (reg:CC FLAGS_REG))])] - "INTVAL (operands[0]) == GET_MODE_SIZE (word_mode)" + "TARGET_POP_SCRATCH_REGISTER + && INTVAL (operands[0]) == GET_MODE_SIZE (word_mode)" [(set (match_dup 1) (mem:W (post_inc:P (reg:P SP_REG))))]) ;; Two pops case is tricky, since pop causes dependency @@ -18686,7 +18722,8 @@ (plus:P (reg:P SP_REG) (match_operand:P 0 "const_int_operand"))) (clobber (reg:CC FLAGS_REG))])] - "INTVAL (operands[0]) == 2*GET_MODE_SIZE (word_mode)" + "TARGET_POP_SCRATCH_REGISTER + && INTVAL (operands[0]) == 2*GET_MODE_SIZE (word_mode)" [(set (match_dup 1) (mem:W (post_inc:P (reg:P SP_REG)))) (set (match_dup 2) (mem:W (post_inc:P (reg:P SP_REG))))]) @@ -18697,6 +18734,7 @@ (match_operand:P 0 "const_int_operand"))) (clobber (reg:CC FLAGS_REG))])] "optimize_insn_for_size_p () + && TARGET_POP_SCRATCH_REGISTER && INTVAL (operands[0]) == 2*GET_MODE_SIZE (word_mode)" [(set (match_dup 1) (mem:W (post_inc:P (reg:P SP_REG)))) (set (match_dup 1) (mem:W (post_inc:P (reg:P SP_REG))))]) diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt index e7fbf9b6f99..9fde117ccad 100644 --- a/gcc/config/i386/i386.opt +++ b/gcc/config/i386/i386.opt @@ -1063,3 +1063,26 @@ Support WAITPKG built-in functions and code generation. mcldemote Target Report Mask(ISA_CLDEMOTE) Var(ix86_isa_flags2) Save Support CLDEMOTE built-in functions and code generation. + +mzero-caller-saved-regs= +Target Report RejectNegative Joined Enum(zero_caller_saved_regs) Var(ix86_zero_caller_saved_regs) Init(zero_caller_saved_regs_skip) +Clear caller-saved registers upon function return. + +Enum +Name(zero_caller_saved_regs) Type(enum zero_caller_saved_regs) +Known choices of clearing caller-saved registers upon function return (for use with the -mzero-caller-saved-regs= option): + +EnumValue +Enum(zero_caller_saved_regs) String(skip) Value(zero_caller_saved_regs_skip) + +EnumValue +Enum(zero_caller_saved_regs) String(used-gpr) Value(zero_caller_saved_regs_used_gpr) + +EnumValue +Enum(zero_caller_saved_regs) String(all-gpr) Value(zero_caller_saved_regs_all_gpr) + +EnumValue +Enum(zero_caller_saved_regs) String(used) Value(zero_caller_saved_regs_used) + +EnumValue +Enum(zero_caller_saved_regs) String(all) Value(zero_caller_saved_regs_all) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 7aeb4fd4d27..71b329ab365 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -5950,6 +5950,18 @@ The @code{indirect_return} attribute can be applied to a function, as well as variable or type of function pointer to inform the compiler that the function may return via indirect branch. +@item zero_caller_saved_regs("@var{choice}") +@cindex @code{zero_caller_saved_regs} function attribute, x86 +On x86 targets, the @code{zero_caller_saved_regs} attribute causes the +compiler to zero caller-saved integer registers at function return +according to @var{choice}. @samp{skip} doesn't zero caller-saved +registers. @samp{used-gpr} zeros caller-saved integer registers which +are used in function. @samp{all-gpr} zeros all caller-saved integer and +vector registers. @samp{used} zeros caller-saved integer and vector +registers which are used in function. @samp{all} zeros all caller-saved +integer and vector registers. The default for the attribute is +controlled by @option{-mzero-caller-saved-regs}. + @end table On the x86, the inliner does not inline a diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 055e8c4759c..881d79df233 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -1308,7 +1308,7 @@ See RS/6000 and PowerPC Options. -mstack-protector-guard-symbol=@var{symbol} @gol -mgeneral-regs-only -mcall-ms2sysv-xlogues @gol -mindirect-branch=@var{choice} -mfunction-return=@var{choice} @gol --mindirect-branch-register} +-mindirect-branch-register -mzero-caller-saved-regs=@var{choice}} @emph{x86 Windows Options} @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol @@ -28522,6 +28522,18 @@ not be reachable in the large code model. @opindex -mindirect-branch-register Force indirect call and jump via register. +@item -mzero-caller-saved-regs=@var{choice} +@opindex -mzero-caller-saved-regs +Zero caller-saved registers at function return according to +@var{choice}. @samp{skip}, which is the default, doesn't zero +caller-saved registers. @samp{used-gpr} zeros caller-saved integer +registers which are used in function. @samp{all-gpr} zeros all +caller-saved integer and vector registers. @samp{used} zeros +caller-saved integer and vector registers which are used in function. +@samp{all} zeros all caller-saved integer and vector registers. You +can control this behavior for a specific function by using the function +attribute @code{zero_caller_saved_regs}. @xref{Function Attributes}. + @end table These @samp{-m} switches are supported in addition to the above diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c new file mode 100644 index 00000000000..4c9e6d68dab --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=used" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c new file mode 100644 index 00000000000..ea614ecba53 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=skip" } */ + +extern int foo (int) __attribute__ ((zero_caller_saved_regs("all-gpr"))); + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c new file mode 100644 index 00000000000..f19ed7c9a68 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c @@ -0,0 +1,39 @@ +/* { dg-do run { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=used-gpr" } */ + +struct S { int i; }; +__attribute__((const, noinline, noclone)) +struct S foo (int x) +{ + struct S s; + s.i = x; + return s; +} + +int a[2048], b[2048], c[2048], d[2048]; +struct S e[2048]; + +__attribute__((noinline, noclone)) void +bar (void) +{ + int i; + for (i = 0; i < 1024; i++) + { + e[i] = foo (i); + a[i+2] = a[i] + a[i+1]; + b[10] = b[10] + i; + c[i] = c[2047 - i]; + d[i] = d[i + 1]; + } +} + +int +main () +{ + int i; + bar (); + for (i = 0; i < 1024; i++) + if (e[i].i != i) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c new file mode 100644 index 00000000000..f0283d9e750 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c @@ -0,0 +1,39 @@ +/* { dg-do run { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all-gpr" } */ + +struct S { int i; }; +__attribute__((const, noinline, noclone)) +struct S foo (int x) +{ + struct S s; + s.i = x; + return s; +} + +int a[2048], b[2048], c[2048], d[2048]; +struct S e[2048]; + +__attribute__((noinline, noclone)) void +bar (void) +{ + int i; + for (i = 0; i < 1024; i++) + { + e[i] = foo (i); + a[i+2] = a[i] + a[i+1]; + b[10] = b[10] + i; + c[i] = c[2047 - i]; + d[i] = d[i + 1]; + } +} + +int +main () +{ + int i; + bar (); + for (i = 0; i < 1024; i++) + if (e[i].i != i) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c new file mode 100644 index 00000000000..044da02e244 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all -march=corei7" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { target { ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 15 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c new file mode 100644 index 00000000000..31487d51f53 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all -march=corei7 -mavx" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-times "vzeroall" 1 } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c new file mode 100644 index 00000000000..dc561b0c71d --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=skip" } */ + +extern void foo (void) __attribute__ ((zero_caller_saved_regs("used"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c new file mode 100644 index 00000000000..24824b0355e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all" } */ + +extern void foo (void) __attribute__ ((zero_caller_saved_regs("skip"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c new file mode 100644 index 00000000000..9ba4f547401 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=used" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c new file mode 100644 index 00000000000..529adc26ad1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=used -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c new file mode 100644 index 00000000000..ac6201e27c9 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=used -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm2, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c new file mode 100644 index 00000000000..6b9e25abf13 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all-gpr" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c new file mode 100644 index 00000000000..e8e9c781ed1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c @@ -0,0 +1,23 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" { target { ia32 } } } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { target { ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm1, %xmm\[0-9\]+" 14 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c new file mode 100644 index 00000000000..3052eb05503 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=skip -march=corei7" } */ + +__attribute__ ((zero_caller_saved_regs("used"))) +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c new file mode 100644 index 00000000000..71369f56159 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all -march=corei7 -mavx" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c new file mode 100644 index 00000000000..9a31af9516a --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all -march=corei7 -mavx512f" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c new file mode 100644 index 00000000000..a6f8eb7233a --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=skip" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c new file mode 100644 index 00000000000..bada4c73719 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=skip" } */ + +extern void foo (void) __attribute__ ((zero_caller_saved_regs("used-gpr"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c new file mode 100644 index 00000000000..b93719a11df --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c @@ -0,0 +1,20 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=skip" } */ + +__attribute__ ((zero_caller_saved_regs("all-gpr"))) +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c new file mode 100644 index 00000000000..bef1d36eca5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all-gpr" } */ + +extern void foo (void) __attribute__ ((zero_caller_saved_regs("skip"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c new file mode 100644 index 00000000000..73a766c1be9 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=used-gpr" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c new file mode 100644 index 00000000000..cd982ce27db --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=all-gpr" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c new file mode 100644 index 00000000000..23dbed50ab9 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c @@ -0,0 +1,15 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mzero-caller-saved-regs=skip" } */ + +extern int foo (int) __attribute__ ((zero_caller_saved_regs("used-gpr"))); + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ -- 2.17.2