aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog47
-rw-r--r--gcc/common/config/avr/avr-common.c1
-rw-r--r--gcc/config.in8
-rw-r--r--gcc/config/avr/avr-passes.def9
-rw-r--r--gcc/config/avr/avr-protos.h1
-rw-r--r--gcc/config/avr/avr.c318
-rw-r--r--gcc/config/avr/avr.h20
-rw-r--r--gcc/config/avr/avr.md39
-rw-r--r--gcc/config/avr/avr.opt4
-rw-r--r--gcc/config/avr/gen-avr-mmcu-specs.c5
-rw-r--r--gcc/config/avr/specs.h1
-rwxr-xr-xgcc/configure38
-rw-r--r--gcc/configure.ac10
-rw-r--r--gcc/doc/extend.texi29
-rw-r--r--gcc/doc/invoke.texi15
-rw-r--r--gcc/testsuite/gcc.target/avr/isr-test.h4
16 files changed, 501 insertions, 48 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 2e9afed..73cb4fe 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,50 @@
+2017-07-10 Georg-Johann Lay <avr@gjlay.de>
+
+ 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.
+
2017-07-10 Richard Earnshaw <rearnsha@arm.com>
* 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 eaaf211..4bee9d6 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 73c9f92..89d7108 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 affb99b..340823e 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
<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
diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index 56e1498..5d5524b 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 734ede1..d32cac6 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 d5fc345..579c8fa 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 2cdddb2..d17c0b1 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 1efb1c0..81850e0 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 db17eeb..b923aa4 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 da5df40..4f046a1 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 893f958..98aa62c 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -24820,6 +24820,42 @@ $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
+
+
# 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" >>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 c6a9929..aa980a1 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 d0abd7f..6120524 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 aa848bb..4982f7c 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 4643481..176dbc2 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__