diff mbox

[avr] Fix PR20296 / PR81268: Better ISR prologues / epilogues

Message ID 9319e4bf-21de-cfaf-0068-efcebc6e6a21@gjlay.de
State New
Headers show

Commit Message

Georg-Johann Lay July 10, 2017, 9:59 a.m. UTC
On 08.07.2017 06:58, Denis Chertykov wrote:
> 2017-07-07 18:31 GMT+04:00 Georg-Johann Lay <avr@gjlay.de>:
>> Hi,
>>
>> this patch addresses a very old issue, the non-optimal
>> generation of ISR prologues and epilogues.
>>
>> As GAS now provides the __gcc_isr pseudo instruction to
>> overcome some problems, see
>>
>> https://sourceware.org/bugzilla/show_bug.cgi?id=21683
>>
>> this can now be used to address PR20296.
>>
>>
>> This patch does:
>>
>> * Add a configure test if GAS supports __gcc_isr and -mgcc-isr.
>>
>> * Add new option -mgas-isr-prologues to switch on / off
>>    generating of __gcc_isr in ISR prologues / epilogues.
>>
>> * Switch on the feature per default except for -O0 and -Og.
>>
>> * Add a new no_gccisr function attribute to disable __gcc_isr
>>    generation for individual ISRs.
>>
>> * Add a new pass .avr-gasisr that filters out situations where
>>    __gcc_isr is not appropriate.
>>
>> * Extend prologue and epilogue generation to emit __gcc_isr chunks
>>    during prologue and epilogue(s).
>>
>> * Implement final_postscan_insn to emit final __gcc_isr Done chunk.
>>
>> * Add -mgcc-isr to ASM_SPEC if appropriate.
>>
>>
>> We currently have only 3 torture tests for ISRs, namely
>>
>> gcc.target/avr/torture/isr-*.c
>>
>> All these tests PASS when
>>
>> * Run with -mgas-isr-prologues
>> * Run with -mno-gas-isr-prologues
>> * Run for: atmega8 atmega64 atmega103 atmega2560 atmega128 atxmega128a1
>> attiny40
>>
>> Ok for trunk?
> 
> 
> Please apply.

Finally applied a patch that's a bit different:

* A no-op change that uses a different pass name which might be more
   appropriate with future extensions (this to reduce future delta).

* More conservative handling of the optional reg in __gcc_isr 0,Rx.

Johann

>>
>>          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) <no_gccisr>: 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_maybe_gasisr): Add
>>          INSERT_PASS_BEFORE for it.
>>          * config/avr/avr-protos.h (make_avr_pass_maybe_gasisr): 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_lsb): New static
>> functions.
>>          (avr_attribute_table) <no_gccisr>: Add new function attribute.
>>          (avr_set_current_function) <is_no_gccisr>: Init machine field.
>>          (avr_pass_data_gasisr, avr_pass_maybe_gasisr): New pass data
>>          and rtl_opt_pass.
>>          (make_avr_pass_maybe_gasisr): New function.
>>          (emit_push_sfr) <treg>: 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)
>>          <is_no_gccisr, use_L__stack_usage>: New fields.
>>          <gasisr, gasisr.yes, gasisr.maybe, gasisr.regno>: 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) <asm_gccisr>: Add sub spec.

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) <no_gccisr>: 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) <no_gccisr>: Add new function attribute.
	(avr_set_current_function) <is_no_gccisr>: 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) <treg>: 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)
	<is_no_gccisr, use_L__stack_usage>: New fields.
	<gasisr, gasisr.yes, gasisr.maybe, gasisr.regno>: 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) <asm_gccisr>: Add sub spec.

Comments

Martin Liška Aug. 7, 2017, 8:45 a.m. UTC | #1
Hello.

After this revision I see:

$ ../configure --target=avr-elf --enable-languages=c,c++ --disable-bootstrap

$ make

../../gcc/config/avr/avr.c: In function ‘void avr_option_override()’:
../../gcc/config/avr/avr.c:778:29: error: lvalue required as left operand of assignment
   TARGET_GASISR_PROLOGUES = 0;
                             ^
diff mbox

Patch

Index: common/config/avr/avr-common.c
===================================================================
--- common/config/avr/avr-common.c	(revision 250090)
+++ common/config/avr/avr-common.c	(working copy)
@@ -31,6 +31,7 @@  static const struct default_options avr_
     // 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 }
   };
 
Index: config/avr/avr-passes.def
===================================================================
--- config/avr/avr-passes.def	(revision 250090)
+++ config/avr/avr-passes.def	(working copy)
@@ -17,9 +17,12 @@ 
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */
 
-/* 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
Index: config/avr/avr-protos.h
===================================================================
--- config/avr/avr-protos.h	(revision 250091)
+++ config/avr/avr-protos.h	(working copy)
@@ -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 *);
 
Index: config/avr/avr.c
===================================================================
--- config/avr/avr.c	(revision 250091)
+++ config/avr/avr.c	(working copy)
@@ -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 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 fra
 }
 
 
-/*  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);
 
-      /* Push tmp reg.  */
-      emit_push_byte (AVR_TMP_REGNO, true);
+          // 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 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 *fil
 
   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,
              (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_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
Index: config/avr/avr.h
===================================================================
--- config/avr/avr.h	(revision 250091)
+++ config/avr/avr.h	(working copy)
@@ -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
Index: config/avr/avr.md
===================================================================
--- config/avr/avr.md	(revision 250091)
+++ config/avr/avr.md	(working copy)
@@ -85,6 +85,7 @@  (define_c_enum "unspecv"
   [UNSPECV_PROLOGUE_SAVES
    UNSPECV_EPILOGUE_RESTORES
    UNSPECV_WRITE_SP
+   UNSPECV_GASISR
    UNSPECV_GOTO_RECEIVER
    UNSPECV_ENABLE_IRQS
    UNSPECV_MEMORY_BARRIER
@@ -94,6 +95,12 @@  (define_c_enum "unspecv"
    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 @@  (define_insn "epilogue_restores"
    (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)]
Index: config/avr/avr.opt
===================================================================
--- config/avr/avr.opt	(revision 250090)
+++ config/avr/avr.opt	(working copy)
@@ -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.
Index: config/avr/gen-avr-mmcu-specs.c
===================================================================
--- config/avr/gen-avr-mmcu-specs.c	(revision 250090)
+++ config/avr/gen-avr-mmcu-specs.c	(working copy)
@@ -224,6 +224,11 @@  bool is_arch = NULL == mcu->macro;
            : "\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}");
Index: config/avr/specs.h
===================================================================
--- config/avr/specs.h	(revision 250090)
+++ config/avr/specs.h	(working copy)
@@ -52,6 +52,7 @@  along with GCC; see the file COPYING3.
   "%(asm_arch) "                                \
   "%(asm_relax) "                               \
   "%(asm_rmw) "                                 \
+  "%(asm_gccisr) "                              \
   "%(asm_errata_skip) "
 
 #define LINK_ARCH_SPEC                          \
Index: config.in
===================================================================
--- config.in	(revision 250090)
+++ config.in	(working copy)
@@ -284,6 +284,12 @@  that are supported for each access macro
 #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 @@  that are supported for each access macro
 #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
Index: configure
===================================================================
--- configure	(revision 250091)
+++ configure	(working copy)
@@ -24820,6 +24820,42 @@  $as_echo "#define HAVE_AS_AVR_MRMW_OPTIO
 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
+
+
     # 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.
@@ -25282,6 +25318,7 @@  $as_echo "#define HAVE_AS_SPARC5_VIS4 1"
 
 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" >>co
 
 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 :
Index: configure.ac
===================================================================
--- configure.ac	(revision 250091)
+++ configure.ac	(working copy)
@@ -3817,6 +3817,16 @@  [	.set nomacro
       [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.
Index: doc/extend.texi
===================================================================
--- doc/extend.texi	(revision 250090)
+++ doc/extend.texi	(working copy)
@@ -3815,6 +3815,35 @@  prologue/epilogue sequences generated by
 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
Index: doc/invoke.texi
===================================================================
--- doc/invoke.texi	(revision 250090)
+++ doc/invoke.texi	(working copy)
@@ -661,7 +661,8 @@  -imacros @var{file}  -imultilib @var{dir
 @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
Index: testsuite/gcc.target/avr/isr-test.h
===================================================================
--- testsuite/gcc.target/avr/isr-test.h	(revision 250090)
+++ testsuite/gcc.target/avr/isr-test.h	(working copy)
@@ -175,7 +175,7 @@  static void compare_reginfo (unsigned lo
   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__