From: Georg-Johann Lay Date: Mon, 10 Jul 2017 09:49:18 +0000 (+0000) Subject: Better ISR prologues by supporting GASes __gcc_isr pseudo insn. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=63866e04b770cefcfa0c64b427dc924cce475201;p=gcc.git Better ISR prologues by supporting GASes __gcc_isr pseudo insn. gcc/ Better ISR prologues by supporting GASes __gcc_isr pseudo insn. PR target/20296 PR target/81268 * configure.ac [target=avr]: Add GAS check for -mgcc-isr. (HAVE_AS_AVR_MGCCISR_OPTION): If so, AC_DEFINE it. * config.in: Regenerate. * configure: Regenerate. * doc/extend.texi (AVR Function Attributes) : Document it. * doc/invoke.texi (AVR Options) <-mgas-isr-prologues>: Document it. * config/avr/avr.opt (-mgas-isr-prologues): New option and... (TARGET_GASISR_PROLOGUES): ...target mask. * common/config/avr/avr-common.c (avr_option_optimization_table) [OPT_LEVELS_1_PLUS_NOT_DEBUG]: Set -mgas-isr-prologues. * config/avr/avr-passes.def (avr_pass_pre_proep): Add INSERT_PASS_BEFORE for it. * config/avr/avr-protos.h (make_avr_pass_pre_proep): New proto. * config/avr/avr.c (avr_option_override) [!HAVE_AS_AVR_MGCCISR_OPTION]: Unset TARGET_GASISR_PROLOGUES. (avr_no_gccisr_function_p, avr_hregs_split_reg): New static functions. (avr_attribute_table) : Add new function attribute. (avr_set_current_function) : Init machine field. (avr_pass_data_pre_proep, avr_pass_pre_proep): New pass data and rtl_opt_pass. (make_avr_pass_pre_proep): New function. (emit_push_sfr) : Add argument to function and use it instead of TMP_REG. (avr_expand_prologue) [machine->gasisr.maybe]: Emit gasisr insn and set machine->gasisr.yes. (avr_expand_epilogue) [machine->gasisr.yes]: Similar. (avr_asm_function_end_prologue) [machine->gasisr.yes]: Add __gcc_isr.n_pushed to .L__stack_usage. (TARGET_ASM_FINAL_POSTSCAN_INSN): Define to... (avr_asm_final_postscan_insn): ...this new static function. * config/avr/avr.h (machine_function) : New fields. : New fields. * config/avr/avr.md (UNSPECV_GASISR): Add unspecv enum. (GASISR_Prologue, GASISR_Epilogue, GASISR_Done): New define_constants. (gasisr, *gasisr): New expander and insn. * config/avr/gen-avr-mmcu-specs.c (print_mcu) [HAVE_AS_AVR_MGCCISR_OPTION]: Print asm_gccisr spec. * config/avr/specs.h (ASM_SPEC) : Add sub spec. From-SVN: r250093 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2e9afed564a..73cb4fef085 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,50 @@ +2017-07-10 Georg-Johann Lay + + Better ISR prologues by supporting GASes __gcc_isr pseudo insn. + + PR target/20296 + PR target/81268 + * configure.ac [target=avr]: Add GAS check for -mgcc-isr. + (HAVE_AS_AVR_MGCCISR_OPTION): If so, AC_DEFINE it. + * config.in: Regenerate. + * configure: Regenerate. + * doc/extend.texi (AVR Function Attributes) : Document it. + * doc/invoke.texi (AVR Options) <-mgas-isr-prologues>: Document it. + * config/avr/avr.opt (-mgas-isr-prologues): New option and... + (TARGET_GASISR_PROLOGUES): ...target mask. + * common/config/avr/avr-common.c + (avr_option_optimization_table) [OPT_LEVELS_1_PLUS_NOT_DEBUG]: + Set -mgas-isr-prologues. + * config/avr/avr-passes.def (avr_pass_pre_proep): Add + INSERT_PASS_BEFORE for it. + * config/avr/avr-protos.h (make_avr_pass_pre_proep): New proto. + * config/avr/avr.c (avr_option_override) + [!HAVE_AS_AVR_MGCCISR_OPTION]: Unset TARGET_GASISR_PROLOGUES. + (avr_no_gccisr_function_p, avr_hregs_split_reg): New static functions. + (avr_attribute_table) : Add new function attribute. + (avr_set_current_function) : Init machine field. + (avr_pass_data_pre_proep, avr_pass_pre_proep): New pass data + and rtl_opt_pass. + (make_avr_pass_pre_proep): New function. + (emit_push_sfr) : Add argument to function and use it + instead of TMP_REG. + (avr_expand_prologue) [machine->gasisr.maybe]: Emit gasisr insn + and set machine->gasisr.yes. + (avr_expand_epilogue) [machine->gasisr.yes]: Similar. + (avr_asm_function_end_prologue) [machine->gasisr.yes]: Add + __gcc_isr.n_pushed to .L__stack_usage. + (TARGET_ASM_FINAL_POSTSCAN_INSN): Define to... + (avr_asm_final_postscan_insn): ...this new static function. + * config/avr/avr.h (machine_function) + : New fields. + : New fields. + * config/avr/avr.md (UNSPECV_GASISR): Add unspecv enum. + (GASISR_Prologue, GASISR_Epilogue, GASISR_Done): New define_constants. + (gasisr, *gasisr): New expander and insn. + * config/avr/gen-avr-mmcu-specs.c (print_mcu) + [HAVE_AS_AVR_MGCCISR_OPTION]: Print asm_gccisr spec. + * config/avr/specs.h (ASM_SPEC) : Add sub spec. + 2017-07-10 Richard Earnshaw * config/arm/parsecpu.awk (gen_comm_data): Do not escape single quotes diff --git a/gcc/common/config/avr/avr-common.c b/gcc/common/config/avr/avr-common.c index eaaf211e41c..4bee9d670d9 100644 --- a/gcc/common/config/avr/avr-common.c +++ b/gcc/common/config/avr/avr-common.c @@ -31,6 +31,7 @@ static const struct default_options avr_option_optimization_table[] = // The only effect of -fcaller-saves might be that it triggers // a frame without need when it tries to be smart around calls. { OPT_LEVELS_ALL, OPT_fcaller_saves, NULL, 0 }, + { OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_mgas_isr_prologues, NULL, 1 }, { OPT_LEVELS_NONE, 0, NULL, 0 } }; diff --git a/gcc/config.in b/gcc/config.in index 73c9f92bb5d..89d7108e8db 100644 --- a/gcc/config.in +++ b/gcc/config.in @@ -284,6 +284,12 @@ #endif +/* Define if your avr assembler supports -mgcc-isr option. */ +#ifndef USED_FOR_TARGET +#undef HAVE_AS_AVR_MGCCISR_OPTION +#endif + + /* Define if your avr assembler supports --mlink-relax option. */ #ifndef USED_FOR_TARGET #undef HAVE_AS_AVR_MLINK_RELAX_OPTION @@ -660,11 +666,13 @@ #undef HAVE_AS_SPARC5_VIS4 #endif + /* Define if your assembler supports SPARC6 instructions. */ #ifndef USED_FOR_TARGET #undef HAVE_AS_SPARC6 #endif + /* Define if your assembler and linker support GOTDATA_OP relocs. */ #ifndef USED_FOR_TARGET #undef HAVE_AS_SPARC_GOTDATA_OP diff --git a/gcc/config/avr/avr-passes.def b/gcc/config/avr/avr-passes.def index affb99b4723..340823e15c4 100644 --- a/gcc/config/avr/avr-passes.def +++ b/gcc/config/avr/avr-passes.def @@ -17,9 +17,12 @@ along with GCC; see the file COPYING3. If not see . */ -/* FIXME: We have to add the last pass first, otherwise - gen-pass-instances.awk won't work as expected. */ - +/* An analysis pass that runs prior to prologue / epilogue generation. + Computes cfun->machine->gasisr.maybe which is used in prologue and + epilogue generation provided -mgas-isr-prologues is on. */ + +INSERT_PASS_BEFORE (pass_thread_prologue_and_epilogue, 1, avr_pass_pre_proep); + /* This avr-specific pass (re)computes insn notes, in particular REG_DEAD notes which are used by `avr.c::reg_unused_after' and branch offset computations. These notes must be correct, i.e. there must be no diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 56e149832d5..5d5524b2e65 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -158,6 +158,7 @@ extern bool avr_have_dimode; namespace gcc { class context; } class rtl_opt_pass; +extern rtl_opt_pass *make_avr_pass_pre_proep (gcc::context *); extern rtl_opt_pass *make_avr_pass_recompute_notes (gcc::context *); extern rtl_opt_pass *make_avr_pass_casesi (gcc::context *); diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index 734ede10d6f..d32cac64cd0 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -774,6 +774,10 @@ avr_option_override (void) if (flag_pie == 2) warning (OPT_fPIE, "-fPIE is not supported"); +#if !defined (HAVE_AS_AVR_MGCCISR_OPTION) + TARGET_GASISR_PROLOGUES = 0; +#endif + if (!avr_set_core_architecture()) return; @@ -1007,6 +1011,15 @@ avr_OS_main_function_p (tree func) } +/* Return nonzero if FUNC is a no_gccisr function as specified + by the "no_gccisr" attribute. */ + +static int +avr_no_gccisr_function_p (tree func) +{ + return avr_lookup_function_attribute1 (func, "no_gccisr"); +} + /* Implement `TARGET_SET_CURRENT_FUNCTION'. */ /* Sanity cheching for above function attributes. */ @@ -1030,6 +1043,7 @@ avr_set_current_function (tree decl) cfun->machine->is_interrupt = avr_interrupt_function_p (decl); cfun->machine->is_OS_task = avr_OS_task_function_p (decl); cfun->machine->is_OS_main = avr_OS_main_function_p (decl); + cfun->machine->is_no_gccisr = avr_no_gccisr_function_p (decl); isr = cfun->machine->is_interrupt ? "interrupt" : "signal"; @@ -1220,6 +1234,9 @@ avr_initial_elimination_offset (int from, int to) int offset = frame_pointer_needed ? 2 : 0; int avr_pc_size = AVR_HAVE_EIJMP_EICALL ? 3 : 2; + // If FROM is ARG_POINTER_REGNUM, we are not in an ISR as ISRs + // might not have arguments. Hence the following is not affected + // by gasisr prologues. offset += avr_regs_to_save (NULL); return (get_frame_size () + avr_outgoing_args_size() + avr_pc_size + 1 + offset); @@ -1314,6 +1331,8 @@ avr_return_addr_rtx (int count, rtx tem) else r = gen_rtx_SYMBOL_REF (Pmode, ".L__stack_usage+1"); + cfun->machine->use_L__stack_usage = 1; + r = gen_rtx_PLUS (Pmode, tem, r); r = gen_frame_mem (Pmode, memory_address (Pmode, r)); r = gen_rtx_ROTATE (HImode, r, GEN_INT (8)); @@ -1394,6 +1413,97 @@ sequent_regs_live (void) return (cur_seq == live_seq) ? live_seq : 0; } +namespace { +static const pass_data avr_pass_data_pre_proep = +{ + RTL_PASS, // type + "", // name (will be patched) + OPTGROUP_NONE, // optinfo_flags + TV_DF_SCAN, // tv_id + 0, // properties_required + 0, // properties_provided + 0, // properties_destroyed + 0, // todo_flags_start + 0 // todo_flags_finish +}; + + +class avr_pass_pre_proep : public rtl_opt_pass +{ +public: + avr_pass_pre_proep (gcc::context *ctxt, const char *name) + : rtl_opt_pass (avr_pass_data_pre_proep, ctxt) + { + this->name = name; + } + + void compute_maybe_gasisr (function*); + + virtual unsigned int execute (function *fun) + { + if (TARGET_GASISR_PROLOGUES + // Whether this function is an ISR worth scanning at all. + && !fun->machine->is_no_gccisr + && (fun->machine->is_interrupt + || fun->machine->is_signal) + && !cfun->machine->is_naked + // Paranoia: Non-local gotos and labels that might escape. + && !cfun->calls_setjmp + && !cfun->has_nonlocal_label + && !cfun->has_forced_label_in_static) + { + compute_maybe_gasisr (fun); + } + + return 0; + } + +}; // avr_pass_pre_proep + +} // anon namespace + +rtl_opt_pass* +make_avr_pass_pre_proep (gcc::context *ctxt) +{ + return new avr_pass_pre_proep (ctxt, "avr-pre-proep"); +} + + +/* Set fun->machine->gasisr.maybe provided we don't find anything that + prohibits GAS generating parts of ISR prologues / epilogues for us. */ + +void +avr_pass_pre_proep::compute_maybe_gasisr (function *fun) +{ + // Don't use BB iterators so that we see JUMP_TABLE_DATA. + + for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + // Transparent calls always use [R]CALL and are filtered out by GAS. + // ISRs don't use -mcall-prologues, hence what remains to be filtered + // out are open coded (tail) calls. + + if (CALL_P (insn)) + return; + + // __tablejump2__ clobbers something and is targeted by JMP so + // that GAS won't see its usage. + + if (AVR_HAVE_JMP_CALL + && JUMP_TABLE_DATA_P (insn)) + return; + + // Non-local gotos not seen in *FUN. + + if (JUMP_P (insn) + && find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX)) + return; + } + + fun->machine->gasisr.maybe = 1; +} + + /* Obtain the length sequence of insns. */ int @@ -1418,6 +1528,46 @@ avr_incoming_return_addr_rtx (void) return gen_frame_mem (HImode, plus_constant (Pmode, stack_pointer_rtx, 1)); } + +/* Unset a bit in *SET. If successful, return the respective bit number. + Otherwise, return -1 and *SET is unaltered. */ + +static int +avr_hregs_split_reg (HARD_REG_SET *set) +{ + for (int regno = 0; regno < 32; regno++) + if (TEST_HARD_REG_BIT (*set, regno)) + { + // Don't remove a register from *SET which might indicate that + // some RAMP* register might need ISR prologue / epilogue treatment. + + if (AVR_HAVE_RAMPX + && (REG_X == regno || REG_X + 1 == regno) + && TEST_HARD_REG_BIT (*set, REG_X) + && TEST_HARD_REG_BIT (*set, REG_X + 1)) + continue; + + if (AVR_HAVE_RAMPY + && !frame_pointer_needed + && (REG_Y == regno || REG_Y + 1 == regno) + && TEST_HARD_REG_BIT (*set, REG_Y) + && TEST_HARD_REG_BIT (*set, REG_Y + 1)) + continue; + + if (AVR_HAVE_RAMPZ + && (REG_Z == regno || REG_Z + 1 == regno) + && TEST_HARD_REG_BIT (*set, REG_Z) + && TEST_HARD_REG_BIT (*set, REG_Z + 1)) + continue; + + CLEAR_HARD_REG_BIT (*set, regno); + return regno; + } + + return -1; +} + + /* Helper for expand_prologue. Emit a push of a byte register. */ static void @@ -1438,24 +1588,24 @@ emit_push_byte (unsigned regno, bool frame_related_p) } -/* Helper for expand_prologue. Emit a push of a SFR via tmp_reg. +/* Helper for expand_prologue. Emit a push of a SFR via register TREG. SFR is a MEM representing the memory location of the SFR. If CLR_P then clear the SFR after the push using zero_reg. */ static void -emit_push_sfr (rtx sfr, bool frame_related_p, bool clr_p) +emit_push_sfr (rtx sfr, bool frame_related_p, bool clr_p, int treg) { rtx_insn *insn; gcc_assert (MEM_P (sfr)); - /* IN __tmp_reg__, IO(SFR) */ - insn = emit_move_insn (tmp_reg_rtx, sfr); + /* IN treg, IO(SFR) */ + insn = emit_move_insn (all_regs_rtx[treg], sfr); if (frame_related_p) RTX_FRAME_RELATED_P (insn) = 1; - /* PUSH __tmp_reg__ */ - emit_push_byte (AVR_TMP_REGNO, frame_related_p); + /* PUSH treg */ + emit_push_byte (treg, frame_related_p); if (clr_p) { @@ -1759,37 +1909,66 @@ avr_expand_prologue (void) if (cfun->machine->is_interrupt || cfun->machine->is_signal) { + int treg = AVR_TMP_REGNO; /* Enable interrupts. */ if (cfun->machine->is_interrupt) emit_insn (gen_enable_interrupt ()); - /* Push zero reg. */ - emit_push_byte (AVR_ZERO_REGNO, true); + if (cfun->machine->gasisr.maybe) + { + /* Let GAS PR21472 emit prologue preamble for us which handles SREG, + ZERO_REG and TMP_REG and one additional, optional register for + us in an optimal way. This even scans through inline asm. */ + + cfun->machine->gasisr.yes = 1; + + // The optional reg or TMP_REG if we don't need one. If we need one, + // remove that reg from SET so that it's not puhed / popped twice. + // We also use it below instead of TMP_REG in some places. + + treg = avr_hregs_split_reg (&set); + if (treg < 0) + treg = AVR_TMP_REGNO; + cfun->machine->gasisr.regno = treg; + + // The worst case of pushes. The exact number can be inferred + // at assembly time by magic expression __gcc_isr.n_pushed. + cfun->machine->stack_usage += 3 + (treg != AVR_TMP_REGNO); + + // Emit a Prologue chunk. Epilogue chunk(s) might follow. + // The final Done chunk is emit by final postscan. + emit_insn (gen_gasisr (GEN_INT (GASISR_Prologue), GEN_INT (treg))); + } + else // !TARGET_GASISR_PROLOGUES: Classic, dumb prologue preamble. + { + /* Push zero reg. */ + emit_push_byte (AVR_ZERO_REGNO, true); - /* Push tmp reg. */ - emit_push_byte (AVR_TMP_REGNO, true); + /* Push tmp reg. */ + emit_push_byte (AVR_TMP_REGNO, true); - /* Push SREG. */ - /* ??? There's no dwarf2 column reserved for SREG. */ - emit_push_sfr (sreg_rtx, false, false /* clr */); + /* Push SREG. */ + /* ??? There's no dwarf2 column reserved for SREG. */ + emit_push_sfr (sreg_rtx, false, false /* clr */, AVR_TMP_REGNO); - /* Clear zero reg. */ - emit_move_insn (zero_reg_rtx, const0_rtx); + /* Clear zero reg. */ + emit_move_insn (zero_reg_rtx, const0_rtx); - /* Prevent any attempt to delete the setting of ZERO_REG! */ - emit_use (zero_reg_rtx); + /* Prevent any attempt to delete the setting of ZERO_REG! */ + emit_use (zero_reg_rtx); + } /* Push and clear RAMPD/X/Y/Z if present and low-part register is used. ??? There are no dwarf2 columns reserved for RAMPD/X/Y/Z. */ if (AVR_HAVE_RAMPD) - emit_push_sfr (rampd_rtx, false /* frame-related */, true /* clr */); + emit_push_sfr (rampd_rtx, false /* frame */, true /* clr */, treg); if (AVR_HAVE_RAMPX && TEST_HARD_REG_BIT (set, REG_X) && TEST_HARD_REG_BIT (set, REG_X + 1)) { - emit_push_sfr (rampx_rtx, false /* frame-related */, true /* clr */); + emit_push_sfr (rampx_rtx, false /* frame */, true /* clr */, treg); } if (AVR_HAVE_RAMPY @@ -1797,14 +1976,14 @@ avr_expand_prologue (void) || (TEST_HARD_REG_BIT (set, REG_Y) && TEST_HARD_REG_BIT (set, REG_Y + 1)))) { - emit_push_sfr (rampy_rtx, false /* frame-related */, true /* clr */); + emit_push_sfr (rampy_rtx, false /* frame */, true /* clr */, treg); } if (AVR_HAVE_RAMPZ && TEST_HARD_REG_BIT (set, REG_Z) && TEST_HARD_REG_BIT (set, REG_Z + 1)) { - emit_push_sfr (rampz_rtx, false /* frame-related */, AVR_HAVE_RAMPD); + emit_push_sfr (rampz_rtx, false /* frame */, AVR_HAVE_RAMPD, treg); } } /* is_interrupt is_signal */ @@ -1846,11 +2025,23 @@ avr_asm_function_end_prologue (FILE *file) fprintf (file, "/* frame size = " HOST_WIDE_INT_PRINT_DEC " */\n", get_frame_size()); - fprintf (file, "/* stack size = %d */\n", - cfun->machine->stack_usage); - /* Create symbol stack offset here so all functions have it. Add 1 to stack - usage for offset so that SP + .L__stack_offset = return address. */ - fprintf (file, ".L__stack_usage = %d\n", cfun->machine->stack_usage); + + if (!cfun->machine->gasisr.yes) + { + fprintf (file, "/* stack size = %d */\n", cfun->machine->stack_usage); + // Create symbol stack offset so all functions have it. Add 1 to stack + // usage for offset so that SP + .L__stack_offset = return address. + fprintf (file, ".L__stack_usage = %d\n", cfun->machine->stack_usage); + } + else + { + int used_by_gasisr = 3 + (cfun->machine->gasisr.regno != AVR_TMP_REGNO); + int to = cfun->machine->stack_usage; + int from = to - used_by_gasisr; + // Number of pushed regs is only known at assembly-time. + fprintf (file, "/* stack size = %d...%d */\n", from , to); + fprintf (file, ".L__stack_usage = %d + __gcc_isr.n_pushed\n", from); + } } @@ -2026,6 +2217,15 @@ avr_expand_epilogue (bool sibcall_p) /* Restore used registers. */ + int treg = AVR_TMP_REGNO; + + if (isr_p + && cfun->machine->gasisr.yes) + { + treg = cfun->machine->gasisr.regno; + CLEAR_HARD_REG_BIT (set, treg); + } + for (int reg = 31; reg >= 0; --reg) if (TEST_HARD_REG_BIT (set, reg)) emit_pop_byte (reg); @@ -2039,8 +2239,8 @@ avr_expand_epilogue (bool sibcall_p) && TEST_HARD_REG_BIT (set, REG_Z) && TEST_HARD_REG_BIT (set, REG_Z + 1)) { - emit_pop_byte (TMP_REGNO); - emit_move_insn (rampz_rtx, tmp_reg_rtx); + emit_pop_byte (treg); + emit_move_insn (rampz_rtx, all_regs_rtx[treg]); } if (AVR_HAVE_RAMPY @@ -2048,34 +2248,43 @@ avr_expand_epilogue (bool sibcall_p) || (TEST_HARD_REG_BIT (set, REG_Y) && TEST_HARD_REG_BIT (set, REG_Y + 1)))) { - emit_pop_byte (TMP_REGNO); - emit_move_insn (rampy_rtx, tmp_reg_rtx); + emit_pop_byte (treg); + emit_move_insn (rampy_rtx, all_regs_rtx[treg]); } if (AVR_HAVE_RAMPX && TEST_HARD_REG_BIT (set, REG_X) && TEST_HARD_REG_BIT (set, REG_X + 1)) { - emit_pop_byte (TMP_REGNO); - emit_move_insn (rampx_rtx, tmp_reg_rtx); + emit_pop_byte (treg); + emit_move_insn (rampx_rtx, all_regs_rtx[treg]); } if (AVR_HAVE_RAMPD) { - emit_pop_byte (TMP_REGNO); - emit_move_insn (rampd_rtx, tmp_reg_rtx); + emit_pop_byte (treg); + emit_move_insn (rampd_rtx, all_regs_rtx[treg]); } - /* Restore SREG using tmp_reg as scratch. */ + if (cfun->machine->gasisr.yes) + { + // Emit an Epilogue chunk. + emit_insn (gen_gasisr (GEN_INT (GASISR_Epilogue), + GEN_INT (cfun->machine->gasisr.regno))); + } + else // !TARGET_GASISR_PROLOGUES + { + /* Restore SREG using tmp_reg as scratch. */ - emit_pop_byte (AVR_TMP_REGNO); - emit_move_insn (sreg_rtx, tmp_reg_rtx); + emit_pop_byte (AVR_TMP_REGNO); + emit_move_insn (sreg_rtx, tmp_reg_rtx); - /* Restore tmp REG. */ - emit_pop_byte (AVR_TMP_REGNO); + /* Restore tmp REG. */ + emit_pop_byte (AVR_TMP_REGNO); - /* Restore zero REG. */ - emit_pop_byte (AVR_ZERO_REGNO); + /* Restore zero REG. */ + emit_pop_byte (AVR_ZERO_REGNO); + } } if (!sibcall_p) @@ -2088,6 +2297,7 @@ avr_expand_epilogue (bool sibcall_p) static void avr_asm_function_begin_epilogue (FILE *file) { + app_disable(); fprintf (file, "/* epilogue start */\n"); } @@ -3094,6 +3304,25 @@ avr_final_prescan_insn (rtx_insn *insn, rtx *operand ATTRIBUTE_UNUSED, (int) INSN_ADDRESSES (INSN_UID (insn))); } + +/* Implement `TARGET_ASM_FINAL_POSTSCAN_INSN'. */ +/* When GAS generates (parts of) ISR prologue / epilogue for us, we must + hint GAS about the end of the code to scan. There migh be code located + after the last epilogue. */ + +static void +avr_asm_final_postscan_insn (FILE *stream, rtx_insn *insn, rtx*, int) +{ + if (cfun->machine->gasisr.yes + && !next_real_insn (insn)) + { + app_disable(); + fprintf (stream, "\t__gcc_isr %d,r%d\n", GASISR_Done, + cfun->machine->gasisr.regno); + } +} + + /* Return 0 if undefined, 1 if always true or always false. */ int @@ -8300,7 +8529,7 @@ avr_out_addto_sp (rtx *op, int *plen) } while (addend++ < 0) - avr_asm_len ("push __zero_reg__", op, plen, 1); + avr_asm_len ("push __tmp_reg__", op, plen, 1); } else if (addend > 0) { @@ -9631,6 +9860,8 @@ avr_attribute_table[] = false }, { "interrupt", 0, 0, true, false, false, avr_handle_fndecl_attribute, false }, + { "no_gccisr", 0, 0, true, false, false, avr_handle_fndecl_attribute, + false }, { "naked", 0, 0, false, true, true, avr_handle_fntype_attribute, false }, { "OS_task", 0, 0, false, true, true, avr_handle_fntype_attribute, @@ -14370,6 +14601,9 @@ avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg, #undef TARGET_ASM_SELECT_SECTION #define TARGET_ASM_SELECT_SECTION avr_asm_select_section +#undef TARGET_ASM_FINAL_POSTSCAN_INSN +#define TARGET_ASM_FINAL_POSTSCAN_INSN avr_asm_final_postscan_insn + #undef TARGET_REGISTER_MOVE_COST #define TARGET_REGISTER_MOVE_COST avr_register_move_cost #undef TARGET_MEMORY_MOVE_COST diff --git a/gcc/config/avr/avr.h b/gcc/config/avr/avr.h index d5fc345bbeb..579c8faa0b5 100644 --- a/gcc/config/avr/avr.h +++ b/gcc/config/avr/avr.h @@ -585,6 +585,26 @@ struct GTY(()) machine_function /* 'true' if the above is_foo predicates are sanity-checked to avoid multiple diagnose for the same function. */ int attributes_checked_p; + + /* 'true' - if current function shall not use '__gcc_isr' pseudo + instructions as specified by the "no_gccisr" attribute. */ + int is_no_gccisr; + + /* Used for `__gcc_isr' pseudo instruction handling of + non-naked ISR prologue / epilogue(s). */ + struct + { + /* 'true' if this function actually uses "*gasisr" insns. */ + int yes; + /* 'true' if this function is allowed to use "*gasisr" insns. */ + int maybe; + /* The register numer as printed by the Done chunk. */ + int regno; + } gasisr; + + /* 'true' if this function references .L__stack_usage like with + __builtin_return_address. */ + int use_L__stack_usage; }; /* AVR does not round pushes, but the existence of this macro is diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 2cdddb25cd0..d17c0b13640 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -85,6 +85,7 @@ [UNSPECV_PROLOGUE_SAVES UNSPECV_EPILOGUE_RESTORES UNSPECV_WRITE_SP + UNSPECV_GASISR UNSPECV_GOTO_RECEIVER UNSPECV_ENABLE_IRQS UNSPECV_MEMORY_BARRIER @@ -94,6 +95,12 @@ UNSPECV_DELAY_CYCLES ]) +;; Chunk numbers for __gcc_isr are hard-coded in GAS. +(define_constants + [(GASISR_Prologue 1) + (GASISR_Epilogue 2) + (GASISR_Done 0) + ]) (include "predicates.md") (include "constraints.md") @@ -5800,6 +5807,38 @@ (set_attr "cc" "clobber") (set_attr "isa" "rjmp,jmp")]) + +;; $0 = Chunk: 1 = Prologue, 2 = Epilogue +;; $1 = Register as printed by chunk 0 (Done) in final postscan. +(define_expand "gasisr" + [(parallel [(unspec_volatile [(match_operand:QI 0 "const_int_operand") + (match_operand:QI 1 "const_int_operand")] + UNSPECV_GASISR) + (set (reg:HI REG_SP) + (unspec_volatile:HI [(reg:HI REG_SP)] UNSPECV_GASISR)) + (set (match_dup 2) + (unspec_volatile:BLK [(match_dup 2)] + UNSPECV_MEMORY_BARRIER))])] + "TARGET_GASISR_PROLOGUES" + { + operands[2] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[2]) = 1; + }) + +(define_insn "*gasisr" + [(unspec_volatile [(match_operand:QI 0 "const_int_operand" "P,K") + (match_operand:QI 1 "const_int_operand" "n,n")] + UNSPECV_GASISR) + (set (reg:HI REG_SP) + (unspec_volatile:HI [(reg:HI REG_SP)] UNSPECV_GASISR)) + (set (match_operand:BLK 2) + (unspec_volatile:BLK [(match_dup 2)] UNSPECV_MEMORY_BARRIER))] + "TARGET_GASISR_PROLOGUES" + "__gcc_isr %0" + [(set_attr "length" "6,5") + (set_attr "cc" "clobber")]) + + ; return (define_insn "return" [(return)] diff --git a/gcc/config/avr/avr.opt b/gcc/config/avr/avr.opt index 1efb1c063b6..81850e037f6 100644 --- a/gcc/config/avr/avr.opt +++ b/gcc/config/avr/avr.opt @@ -26,6 +26,10 @@ mmcu= Target RejectNegative Joined Var(avr_mmcu) MissingArgError(missing device or architecture after %qs) -mmcu=MCU Select the target MCU. +mgas-isr-prologues +Target Report Mask(GASISR_PROLOGUES) +Allow usage of __gcc_isr pseudo instructions in ISR prologues and epilogues. + mn-flash= Target RejectNegative Joined Var(avr_n_flash) UInteger Init(-1) Set the number of 64 KiB flash segments. diff --git a/gcc/config/avr/gen-avr-mmcu-specs.c b/gcc/config/avr/gen-avr-mmcu-specs.c index db17eeb41d7..b923aa4ee3c 100644 --- a/gcc/config/avr/gen-avr-mmcu-specs.c +++ b/gcc/config/avr/gen-avr-mmcu-specs.c @@ -224,6 +224,11 @@ print_mcu (const avr_mcu_t *mcu) : "\t%{mrmw}"); #endif // have avr-as -mrmw +#ifdef HAVE_AS_AVR_MGCCISR_OPTION + fprintf (f, "*asm_gccisr:\n%s\n\n", + "\t%{!mno-gas-isr-prologues: -mgcc-isr}"); +#endif // have avr-as -mgcc-isr + fprintf (f, "*asm_errata_skip:\n%s\n\n", errata_skip ? "\t%{mno-skip-bug}" : "\t%{!mskip-bug: -mno-skip-bug}"); diff --git a/gcc/config/avr/specs.h b/gcc/config/avr/specs.h index da5df4039e9..4f046a11e1f 100644 --- a/gcc/config/avr/specs.h +++ b/gcc/config/avr/specs.h @@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see "%(asm_arch) " \ "%(asm_relax) " \ "%(asm_rmw) " \ + "%(asm_gccisr) " \ "%(asm_errata_skip) " #define LINK_ARCH_SPEC \ diff --git a/gcc/configure b/gcc/configure index 893f9587efa..98aa62c00be 100755 --- a/gcc/configure +++ b/gcc/configure @@ -24817,6 +24817,42 @@ if test $gcc_cv_as_avr_mrmw = yes; then $as_echo "#define HAVE_AS_AVR_MRMW_OPTION 1" >>confdefs.h +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for __gcc_isr pseudo instruction" >&5 +$as_echo_n "checking assembler for __gcc_isr pseudo instruction... " >&6; } +if test "${gcc_cv_as_avr_mgccisr+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + gcc_cv_as_avr_mgccisr=no + if test x$gcc_cv_as != x; then + $as_echo '.text + __gcc_isr 1 + __gcc_isr 2 + __gcc_isr 0,r24 + ' > conftest.s + if { ac_try='$gcc_cv_as $gcc_cv_as_flags -mgcc-isr -o conftest.o conftest.s >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } + then + gcc_cv_as_avr_mgccisr=yes + else + echo "configure: failed program was" >&5 + cat conftest.s >&5 + fi + rm -f conftest.o conftest.s + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_avr_mgccisr" >&5 +$as_echo "$gcc_cv_as_avr_mgccisr" >&6; } +if test $gcc_cv_as_avr_mgccisr = yes; then + +$as_echo "#define HAVE_AS_AVR_MGCCISR_OPTION 1" >>confdefs.h + fi @@ -25282,6 +25318,7 @@ $as_echo "#define HAVE_AS_SPARC5_VIS4 1" >>confdefs.h fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for SPARC6 instructions" >&5 $as_echo_n "checking assembler for SPARC6 instructions... " >&6; } if test "${gcc_cv_as_sparc_sparc6+set}" = set; then : @@ -25318,6 +25355,7 @@ $as_echo "#define HAVE_AS_SPARC6 1" >>confdefs.h fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for LEON instructions" >&5 $as_echo_n "checking assembler for LEON instructions... " >&6; } if test "${gcc_cv_as_sparc_leon+set}" = set; then : diff --git a/gcc/configure.ac b/gcc/configure.ac index c6a9929a093..aa980a1c426 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -3817,6 +3817,16 @@ AS_HELP_STRING([--disable-fix-cortex-a53-843419], [AC_DEFINE(HAVE_AS_AVR_MRMW_OPTION, 1, [Define if your avr assembler supports -mrmw option.])]) + gcc_GAS_CHECK_FEATURE([__gcc_isr pseudo instruction], + gcc_cv_as_avr_mgccisr,, + [-mgcc-isr], [.text + __gcc_isr 1 + __gcc_isr 2 + __gcc_isr 0,r24 + ],, + [AC_DEFINE(HAVE_AS_AVR_MGCCISR_OPTION, 1, + [Define if your avr assembler supports -mgcc-isr option.])]) + # Check how default linker description file implements .rodata for # avrxmega3 (PR21472). avr-gcc assumes .rodata is *not* loaded to # RAM so avr-gcc skips __do_copy_data for .rodata objects. diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index d0abd7faadf..61205240110 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3815,6 +3815,35 @@ prologue/epilogue sequences generated by the compiler. Only basic basic @code{asm} and C code may appear to work, they cannot be depended upon to work reliably and are not supported. +@item no_gccisr +@cindex @code{no_gccisr} function attribute, AVR +Do not use @code{__gcc_isr} pseudo instructions in a function with +the @code{interrupt} or @code{signal} attribute aka. interrupt +service routine (ISR). +For details on @code{__gcc_isr}, see the GNU Binutils +@w{@uref{https://sourceware.org/binutils/docs/as/AVR_002dDependent.html,AVR assembler manual}}. +Use this attribute if the preamble of the ISR prologue should always read +@example +push __zero_reg__ +push __tmp_reg__ +in __tmp_reg__, __SREG__ +push __tmp_reg__ +clr __zero_reg__ +@end example +and accordingly for the postamble of the epilogue --- no matter whether +the mentioned registers are actually used in the ISR or not. +Situations where you might want to use this attribute include: +@itemize @bullet +@item +Code that (effectively) clobbers bits of @code{SREG} other than the +@code{I}-flag by writing to the memory location of @code{SREG}. +@item +Code that uses inline assembler to jump to a different function which +expects (parts of) the prologue code as outlined above to be present. +@end itemize +To disable @code{__gcc_isr} generation for the whole compilation unit, +there is option @option{-mno-gas-isr-prologues}, @pxref{AVR Options}. + @item OS_main @itemx OS_task @cindex @code{OS_main} function attribute, AVR diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index aa848bb2348..4982f7c5d2d 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -661,7 +661,8 @@ Objective-C and Objective-C++ Dialects}. @emph{AVR Options} @gccoptlist{-mmcu=@var{mcu} -mabsdata -maccumulate-args @gol -mbranch-cost=@var{cost} @gol --mcall-prologues -mint8 -mn_flash=@var{size} -mno-interrupts @gol +-mcall-prologues -mgas-isr-prologues -mint8 @gol +-mn_flash=@var{size} -mno-interrupts @gol -mrelax -mrmw -mstrict-X -mtiny-stack -mfract-convert-truncate @gol -mshort-calls -nodevicelib @gol -Waddr-space-convert -Wmisspelled-isr} @@ -15977,6 +15978,18 @@ integers. The default branch cost is 0. Functions prologues/epilogues are expanded as calls to appropriate subroutines. Code size is smaller. +@item -mgas-isr-prologues +@opindex mgas-isr-prologues +Interrupt service routines (ISRs) may use the @code{__gcc_isr} pseudo +instruction supported by GNU Binutils, see the +@w{@uref{https://sourceware.org/binutils/docs/as/AVR_002dDependent.html,AVR assembler manual}} +for details. +If this option is on, the feature can still be disabled for individual +ISRs by means of the @ref{AVR Function Attributes,,@code{no_gccisr}} +function attribute. This feature is activated per default +if optimization is on (but not with @option{-Og}, @pxref{Optimize Options}), +and if GNU Binutils support @w{@uref{https://sourceware.org/PR21683,PR21683}}. + @item -mint8 @opindex mint8 Assume @code{int} to be 8-bit integer. This affects the sizes of all types: a diff --git a/gcc/testsuite/gcc.target/avr/isr-test.h b/gcc/testsuite/gcc.target/avr/isr-test.h index 464348107f7..176dbc2a15f 100644 --- a/gcc/testsuite/gcc.target/avr/isr-test.h +++ b/gcc/testsuite/gcc.target/avr/isr-test.h @@ -175,7 +175,7 @@ static void compare_reginfo (unsigned long gpr_ignore) ST(24,M) ST(25,M) ST(26,M) ST(27,M) \ ST(28,M) ST(29,M) ST(30,M) ST(31,M) -__attribute__((used,naked,noinline,noclone)) +__attribute__((unused,naked,noinline,noclone)) static void host_store1 (void) { __asm __volatile__ @@ -217,7 +217,7 @@ static void host_store1 (void) : "memory", "r31"); } -__attribute__((used,naked,noinline,noclone)) +__attribute__((unused,naked,noinline,noclone)) static void host_store2 (void) { __asm __volatile__