diff options
Diffstat (limited to 'gcc/function.c')
-rw-r--r-- | gcc/function.c | 190 |
1 files changed, 189 insertions, 1 deletions
diff --git a/gcc/function.c b/gcc/function.c index f903a1e..004fa38 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -46,10 +46,12 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "expmed.h" #include "optabs.h" +#include "opts.h" #include "regs.h" #include "emit-rtl.h" #include "recog.h" #include "rtl-error.h" +#include "hard-reg-set.h" #include "alias.h" #include "fold-const.h" #include "stor-layout.h" @@ -5815,6 +5817,103 @@ make_prologue_seq (void) return seq; } +/* Emit a sequence of insns to zero the call-used registers before RET + according to ZERO_REGS_TYPE. */ + +static void +gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type) +{ + bool only_gpr = true; + bool only_used = true; + bool only_arg = true; + + /* No need to zero call-used-regs in main (). */ + if (MAIN_NAME_P (DECL_NAME (current_function_decl))) + return; + + /* No need to zero call-used-regs if __builtin_eh_return is called + since it isn't a normal function return. */ + if (crtl->calls_eh_return) + return; + + /* If only_gpr is true, only zero call-used registers that are + general-purpose registers; if only_used is true, only zero + call-used registers that are used in the current function; + if only_arg is true, only zero call-used registers that pass + parameters defined by the flatform's calling conversion. */ + + using namespace zero_regs_flags; + + only_gpr = zero_regs_type & ONLY_GPR; + only_used = zero_regs_type & ONLY_USED; + only_arg = zero_regs_type & ONLY_ARG; + + /* For each of the hard registers, we should zero it if: + 1. it is a call-used register; + and 2. it is not a fixed register; + and 3. it is not live at the return of the routine; + and 4. it is general registor if only_gpr is true; + and 5. it is used in the routine if only_used is true; + and 6. it is a register that passes parameter if only_arg is true. */ + + /* First, prepare the data flow information. */ + basic_block bb = BLOCK_FOR_INSN (ret); + auto_bitmap live_out; + bitmap_copy (live_out, df_get_live_out (bb)); + df_simulate_initialize_backwards (bb, live_out); + df_simulate_one_insn_backwards (bb, ret, live_out); + + HARD_REG_SET selected_hardregs; + CLEAR_HARD_REG_SET (selected_hardregs); + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (!crtl->abi->clobbers_full_reg_p (regno)) + continue; + if (fixed_regs[regno]) + continue; + if (REGNO_REG_SET_P (live_out, regno)) + continue; + if (only_gpr + && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno)) + continue; + if (only_used && !df_regs_ever_live_p (regno)) + continue; + if (only_arg && !FUNCTION_ARG_REGNO_P (regno)) + continue; + + /* Now this is a register that we might want to zero. */ + SET_HARD_REG_BIT (selected_hardregs, regno); + } + + if (hard_reg_set_empty_p (selected_hardregs)) + return; + + /* Now that we have a hard register set that needs to be zeroed, pass it to + target to generate zeroing sequence. */ + HARD_REG_SET zeroed_hardregs; + start_sequence (); + zeroed_hardregs = targetm.calls.zero_call_used_regs (selected_hardregs); + rtx_insn *seq = get_insns (); + end_sequence (); + if (seq) + { + /* Emit the memory blockage and register clobber asm volatile before + the whole sequence. */ + start_sequence (); + expand_asm_reg_clobber_mem_blockage (zeroed_hardregs); + rtx_insn *seq_barrier = get_insns (); + end_sequence (); + + emit_insn_before (seq_barrier, ret); + emit_insn_before (seq, ret); + + /* Update the data flow information. */ + crtl->must_be_zero_on_return |= zeroed_hardregs; + df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun)); + } +} + + /* Return a sequence to be used as the epilogue for the current function, or NULL. */ @@ -6486,7 +6585,96 @@ make_pass_thread_prologue_and_epilogue (gcc::context *ctxt) { return new pass_thread_prologue_and_epilogue (ctxt); } - + +namespace { + +const pass_data pass_data_zero_call_used_regs = +{ + RTL_PASS, /* type */ + "zero_call_used_regs", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_zero_call_used_regs: public rtl_opt_pass +{ +public: + pass_zero_call_used_regs (gcc::context *ctxt) + : rtl_opt_pass (pass_data_zero_call_used_regs, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *); + +}; // class pass_zero_call_used_regs + +unsigned int +pass_zero_call_used_regs::execute (function *fun) +{ + using namespace zero_regs_flags; + unsigned int zero_regs_type = UNSET; + + tree attr_zero_regs = lookup_attribute ("zero_call_used_regs", + DECL_ATTRIBUTES (fun->decl)); + + /* Get the type of zero_call_used_regs from function attribute. + We have filtered out invalid attribute values already at this point. */ + if (attr_zero_regs) + { + /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE + is the attribute argument's value. */ + attr_zero_regs = TREE_VALUE (attr_zero_regs); + gcc_assert (TREE_CODE (attr_zero_regs) == TREE_LIST); + attr_zero_regs = TREE_VALUE (attr_zero_regs); + gcc_assert (TREE_CODE (attr_zero_regs) == STRING_CST); + + for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i) + if (strcmp (TREE_STRING_POINTER (attr_zero_regs), + zero_call_used_regs_opts[i].name) == 0) + { + zero_regs_type = zero_call_used_regs_opts[i].flag; + break; + } + } + + if (!zero_regs_type) + zero_regs_type = flag_zero_call_used_regs; + + /* No need to zero call-used-regs when no user request is present. */ + if (!(zero_regs_type & ENABLED)) + return 0; + + edge_iterator ei; + edge e; + + /* This pass needs data flow information. */ + df_analyze (); + + /* Iterate over the function's return instructions and insert any + register zeroing required by the -fzero-call-used-regs command-line + option or the "zero_call_used_regs" function attribute. */ + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + { + rtx_insn *insn = BB_END (e->src); + if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn))) + gen_call_used_regs_seq (insn, zero_regs_type); + } + + return 0; +} + +} // anon namespace + +rtl_opt_pass * +make_pass_zero_call_used_regs (gcc::context *ctxt) +{ + return new pass_zero_call_used_regs (ctxt); +} /* If CONSTRAINT is a matching constraint, then return its number. Otherwise, return -1. */ |