This is the mail archive of the binutils-cvs@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Add support for a __gcc_isr pseudo isntruction to the AVR assembler.


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=32f76c677333510350f21a40db062a8d17995c53

commit 32f76c677333510350f21a40db062a8d17995c53
Author: Georg-Johann Lay <avr@gjlay.de>
Date:   Fri Jun 30 16:37:39 2017 +0100

    Add support for a __gcc_isr pseudo isntruction to the AVR assembler.
    
        PR gas/21683
    include * opcode/avr.h (AVR_INSN): Add one for __gcc_isr.
    
    gas * doc/c-avr.texi (AVR Options) <-mgcc-isr>: Document it.
        (AVR Pseudo Instructions): New node.
        * config/tc-avr.h (md_pre_output_hook): Define to avr_pre_output_hook.
        (md_undefined_symbol): Define to avr_undefined_symbol.
        (avr_pre_output_hook, avr_undefined_symbol): New protos.
        * config/tc-avr.c (struc-symbol.h): Include it.
        (ISR_CHUNK_Done, ISR_CHUNK_Prologue, ISR_CHUNK_Epilogue): New enums.
        (avr_isr, avr_gccisr_opcode)
        (avr_no_sreg_hash, avr_no_sreg): New static variables.
        (avr_opt_s) <have_gccisr>: Add field.
        (avr_opt): Add initializer for have_gccisr.
        (enum options) <OPTION_HAVE_GCCISR>: Add enum.
        (md_longopts) <"mgcc-isr">: Add entry.
        (md_show_usage): Document -mgcc-isr.
        (md_parse_option) [OPTION_HAVE_GCCISR]: Handle it.
        (md_undefined_symbol): Remove.
        (avr_undefined_symbol, avr_pre_output_hook): New fuctions.
        (md_begin) <avr_no_sreg_hash, avr_gccisr_opcode>: Initialize them.
        (avr_operand) <pregno>: Add argument and set *pregno if function
        is called for a register constraint.
        [N]: Handle constraint.
        (avr_operands) <avr_operand>: Pass 5th parameter to calls.
        [avr_opt.have_gccisr]: Call avr_update_gccisr.  Call
        avr_gccisr_operands instead of avr_operands.
        (avr_update_gccisr, avr_emit_insn, avr_patch_gccisr_frag)
        (avr_gccisr_operands, avr_check_gccisr_done): New static functions.
        * testsuite/gas/avr/gccisr-01.d: New test.
        * testsuite/gas/avr/gccisr-01.s: New test.
        * testsuite/gas/avr/gccisr-02.d: New test.
        * testsuite/gas/avr/gccisr-02.s: New test.
        * testsuite/gas/avr/gccisr-03.d: New test.
        * testsuite/gas/avr/gccisr-03.s: New test.

Diff:
---
 gas/ChangeLog                     |  36 +++
 gas/config/tc-avr.c               | 594 +++++++++++++++++++++++++++++++++++++-
 gas/config/tc-avr.h               |   6 +
 gas/doc/c-avr.texi                |  62 ++++
 gas/testsuite/gas/avr/gccisr-01.d | 141 +++++++++
 gas/testsuite/gas/avr/gccisr-01.s | 127 ++++++++
 gas/testsuite/gas/avr/gccisr-02.d |  43 +++
 gas/testsuite/gas/avr/gccisr-02.s |  38 +++
 gas/testsuite/gas/avr/gccisr-03.d |   4 +
 gas/testsuite/gas/avr/gccisr-03.s |   6 +
 include/ChangeLog                 |   5 +
 include/opcode/avr.h              |   5 +
 12 files changed, 1060 insertions(+), 7 deletions(-)

diff --git a/gas/ChangeLog b/gas/ChangeLog
index ffc661e..9989f31 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,39 @@
+2017-06-30  Georg-Johann Lay  <avr@gjlay.de>
+
+	PR gas/21683
+	* doc/c-avr.texi (AVR Options) <-mgcc-isr>: Document it.
+	(AVR Pseudo Instructions): New node.
+	* config/tc-avr.h (md_pre_output_hook): Define to avr_pre_output_hook.
+	(md_undefined_symbol): Define to avr_undefined_symbol.
+	(avr_pre_output_hook, avr_undefined_symbol): New protos.
+	* config/tc-avr.c (struc-symbol.h): Include it.
+	(ISR_CHUNK_Done, ISR_CHUNK_Prologue, ISR_CHUNK_Epilogue): New enums.
+	(avr_isr, avr_gccisr_opcode)
+	(avr_no_sreg_hash, avr_no_sreg): New static variables.
+	(avr_opt_s) <have_gccisr>: Add field.
+	(avr_opt): Add initializer for have_gccisr.
+	(enum options) <OPTION_HAVE_GCCISR>: Add enum.
+	(md_longopts) <"mgcc-isr">: Add entry.
+	(md_show_usage): Document -mgcc-isr.
+	(md_parse_option) [OPTION_HAVE_GCCISR]: Handle it.
+	(md_undefined_symbol): Remove.
+	(avr_undefined_symbol, avr_pre_output_hook): New fuctions.
+	(md_begin) <avr_no_sreg_hash, avr_gccisr_opcode>: Initialize them.
+	(avr_operand) <pregno>: Add argument and set *pregno if function
+	is called for a register constraint.
+	[N]: Handle constraint.
+	(avr_operands) <avr_operand>: Pass 5th parameter to calls.
+	[avr_opt.have_gccisr]: Call avr_update_gccisr.  Call
+	avr_gccisr_operands instead of avr_operands.
+	(avr_update_gccisr, avr_emit_insn, avr_patch_gccisr_frag)
+	(avr_gccisr_operands, avr_check_gccisr_done): New static functions.
+	* testsuite/gas/avr/gccisr-01.d: New test.
+	* testsuite/gas/avr/gccisr-01.s: New test.
+	* testsuite/gas/avr/gccisr-02.d: New test.
+	* testsuite/gas/avr/gccisr-02.s: New test.
+	* testsuite/gas/avr/gccisr-03.d: New test.
+	* testsuite/gas/avr/gccisr-03.s: New test.
+
 2017-06-30  Maciej W. Rozycki  <macro@imgtec.com>
 
 	* config/tc-mips.c (match_float_constant): Update description.
diff --git a/gas/config/tc-avr.c b/gas/config/tc-avr.c
index 79837c8..7e3f719 100644
--- a/gas/config/tc-avr.c
+++ b/gas/config/tc-avr.c
@@ -23,6 +23,7 @@
 #include "as.h"
 #include "safe-ctype.h"
 #include "subsegs.h"
+#include "struc-symbol.h"
 #include "dwarf2dbg.h"
 #include "dw2gencfi.h"
 #include "elf/avr.h"
@@ -54,6 +55,107 @@ struct avr_opcodes_s avr_opcodes[] =
   {NULL, NULL, NULL, 0, 0, 0}
 };
 
+
+/* Stuff for the `__gcc_isr' pseudo instruction.
+
+   Purpose of the pseudo instruction is to emit more efficient ISR prologues
+   and epilogues than GCC currently does.  GCC has no explicit (on RTL level)
+   modelling of SREG, TMP_REG or ZERO_REG.  These regs are used implicitly
+   during instruction printing.  That doesn't hurt too much for ordinary
+   functions, however for small ISRs there might be some overhead.
+
+   As implementing http://gcc.gnu.org/PR20296 would imply an almost complete
+   rewite of GCC's AVR back-end (which might pop up less optimized code in
+   other places), we provide a pseudo-instruction which is resolved by GAS
+   into ISR prologue / epilogue as expected by GCC.
+
+   Using GAS for this purpose has the additional benefit that it can scan
+   code emit by inline asm which is opaque to GCC.
+
+   The pseudo-instruction is only supposed to handle the starting of
+   prologue and the ending of epilogues (without RETI) which deal with
+   SREG, TMP_REG and ZERO_REG and one additional, optional general purpose
+   register.
+
+   __gcc_isr consists of 3 different "chunks":
+
+   __gcc_isr 1
+	Chunk 1 (ISR_CHUNK_Prologue)
+	Start the ISR code.  Will be replaced by ISR prologue by next Done chunk.
+	Must be the 1st chunk in a file or follow a Done chunk from previous
+	ISR (which has been patched already).
+
+	It will finish the current frag and emit a new frag of
+	type rs_machine_dependent, subtype ISR_CHUNK_Prologue.
+
+   __gcc_isr 2
+	Chunk 2 (ISR_CHUNK_Epilogue)
+	Will be replaced by ISR epilogue by next Done chunk. Must follow
+	chunk 1 (Prologue) or chunk 2 (Epilogue).  Functions might come
+	without epilogue or with more than one epilogue, and even code
+	located statically after the last epilogue might belong to a function.
+
+	It will finish the current frag and emit a new frag of
+	type rs_machine_dependent, subtype ISR_CHUNK_Epilogue.
+
+   __gcc_isr 0, Rx
+	Chunk 0 (ISR_CHUNK_Done)
+	Must follow chunk 1 (Prologue) or chunk 2 (Epilogue) and finishes
+	the ISR code.  Only GCC can know where a function's code ends.
+
+	It triggers the patch-up of all rs_machine_dependent frags in the
+	current frag chain and turns them into ordinary rs_fill code frags.
+
+	If Rx is a register > ZERO_REG then GCC also wants to push / pop Rx.
+	If neither TMP_REG nor ZERO_REG are needed, Rx will be used in
+	the push / pop sequence avoiding the need for TMP_REG / ZERO_REG.
+	If Rx <= ZERO_REG then GCC doesn't assume anything about Rx.
+
+   Assumptions:
+
+	o  GCC takes care of code that is opaque to GAS like tail calls
+	or non-local goto.
+
+	o  Using SEI / CLI does not count as clobbering SREG.  This is
+	because a final RETI will restore the I-flag.
+
+	o  Using OUT or ST* is supposed not to clobber SREG.  Sequences like
+
+		IN-SREG  +  CLI  +  Atomic-Code  +  OUT-SREG
+
+	will still work as expected because the scan will reveal any
+	clobber of SREG other than I-flag and emit PUSH / POP of SREG.
+*/
+
+enum
+  {
+    ISR_CHUNK_Done = 0,
+    ISR_CHUNK_Prologue = 1,
+    ISR_CHUNK_Epilogue = 2
+  };
+
+static struct
+{
+  /* Previous __gcc_isr chunk (one of the enums above)
+     and it's location for diagnostics.  */
+  int prev_chunk;
+  unsigned line;
+  const char *file;
+  /* Replacer for __gcc_isr.n_pushed once we know how many regs are
+     pushed by the Prologue chunk.  */
+  symbolS *sym_n_pushed;
+
+  /* Set and used during parse from chunk 1 (Prologue) up to chunk 0 (Done).
+     Set by `avr_update_gccisr' and used by `avr_patch_gccisr_frag'.  */
+  int need_reg_tmp;
+  int need_reg_zero;
+  int need_sreg;
+} avr_isr;
+
+static void avr_gccisr_operands (struct avr_opcodes_s*, char**);
+static void avr_update_gccisr (struct avr_opcodes_s*, int, int);
+static struct avr_opcodes_s *avr_gccisr_opcode;
+
 const char comment_chars[] = ";";
 const char line_comment_chars[] = "#";
 const char line_separator_chars[] = "$";
@@ -359,9 +461,10 @@ struct avr_opt_s
   int no_wrap;      /* -mno-wrap: reject rjmp/rcall with 8K wrap-around.  */
   int no_link_relax;   /* -mno-link-relax / -mlink-relax: generate (or not)
                           relocations for linker relaxation.  */
+  int have_gccisr;      /* Whether "__gcc_isr" is a known (pseudo) insn.  */
 };
 
-static struct avr_opt_s avr_opt = { 0, 0, 0, 0 };
+static struct avr_opt_s avr_opt = { 0, 0, 0, 0, 0 };
 
 const char EXP_CHARS[] = "eE";
 const char FLT_CHARS[] = "dD";
@@ -416,6 +519,33 @@ static struct hash_control *avr_hash;
 /* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx).  */
 static struct hash_control *avr_mod_hash;
 
+/* Whether some opcode does not change SREG.  */
+static struct hash_control *avr_no_sreg_hash;
+
+static const char* const avr_no_sreg[] =
+  {
+    /* Arithmetic */
+    "ldi", "swap", "mov", "movw",
+    /* Special instructions.  I-Flag will be restored by RETI, and we don't
+       consider I-Flag as being clobbered when changed.  */
+    "sei", "cli", "reti", "brie", "brid",
+    "nop", "wdr", "sleep",
+    /* Load / Store */
+    "ld", "ldd", "lds", "pop",  "in", "lpm", "elpm",
+    "st", "std", "sts", "push", "out",
+    /* Jumps and Calls.  Calls might call code that changes SREG.
+       GCC has to filter out ABI calls.  The non-ABI transparent calls
+       must use [R]CALL and are filtered out now by not mentioning them.  */
+    "rjmp", "jmp", "ijmp", "ret",
+    /* Skipping.  Branches need SREG to be set, hence we regard them
+       as if they changed SREG and don't list them here.  */
+    "sbrc", "sbrs", "sbic", "sbis", "cpse",
+    /* I/O Manipulation */
+    "sbi", "cbi",
+    /* Read-Modify-Write */
+    "lac", "las", "lat", "xch"
+  };
+
 #define OPTION_MMCU 'm'
 enum options
 {
@@ -424,7 +554,8 @@ enum options
   OPTION_NO_WRAP,
   OPTION_ISA_RMW,
   OPTION_LINK_RELAX,
-  OPTION_NO_LINK_RELAX
+  OPTION_NO_LINK_RELAX,
+  OPTION_HAVE_GCCISR
 };
 
 struct option md_longopts[] =
@@ -436,6 +567,7 @@ struct option md_longopts[] =
   { "mrmw",         no_argument, NULL, OPTION_ISA_RMW     },
   { "mlink-relax",  no_argument, NULL, OPTION_LINK_RELAX  },
   { "mno-link-relax",  no_argument, NULL, OPTION_NO_LINK_RELAX  },
+  { "mgcc-isr",     no_argument, NULL, OPTION_HAVE_GCCISR },
   { NULL, no_argument, NULL, 0 }
 };
 
@@ -544,6 +676,7 @@ md_show_usage (FILE *stream)
 	"  -mrmw            accept Read-Modify-Write instructions\n"
 	"  -mlink-relax     generate relocations for linker relaxation (default)\n"
 	"  -mno-link-relax  don't generate relocations for linker relaxation.\n"
+	"  -mgcc-isr        accept the __gcc_isr pseudo-instruction.\n"
         ));
   show_mcu_list (stream);
 }
@@ -610,14 +743,38 @@ md_parse_option (int c, const char *arg)
     case OPTION_NO_LINK_RELAX:
       avr_opt.no_link_relax = 1;
       return 1;
+    case OPTION_HAVE_GCCISR:
+      avr_opt.have_gccisr = 1;
+      return 1;
     }
 
   return 0;
 }
 
+
+/* Implement `md_undefined_symbol' */
+/* If we are in `__gcc_isr' chunk, pop up `__gcc_isr.n_pushed.<NUM>'
+   instead of `__gcc_isr.n_pushed'.  This will be resolved by the Done
+   chunk in `avr_patch_gccisr_frag' to the number of PUSHes produced by
+   the Prologue chunk.  */
+
 symbolS *
-md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+avr_undefined_symbol (char *name)
 {
+  if (ISR_CHUNK_Done != avr_isr.prev_chunk
+      && 0 == strcmp (name, "__gcc_isr.n_pushed"))
+    {
+      if (!avr_isr.sym_n_pushed)
+	{
+	  static unsigned suffix;
+	  char xname[30];
+	  sprintf (xname, "%s.%03u", name, (++suffix) % 1000);
+	  avr_isr.sym_n_pushed = symbol_new (xname, undefined_section,
+					     (valueT) 0, &zero_address_frag);
+	}
+      return avr_isr.sym_n_pushed;
+    }
+
   return NULL;
 }
 
@@ -659,6 +816,17 @@ md_begin (void)
       hash_insert (avr_mod_hash, EXP_MOD_NAME (i), m.ptr);
     }
 
+  avr_no_sreg_hash = hash_new ();
+
+  for (i = 0; i < ARRAY_SIZE (avr_no_sreg); ++i)
+    {
+      gas_assert (hash_find (avr_hash, avr_no_sreg[i]));
+      hash_insert (avr_no_sreg_hash, avr_no_sreg[i], (char*) 4 /* dummy */);
+    }
+
+  avr_gccisr_opcode = (struct avr_opcodes_s*) hash_find (avr_hash, "__gcc_isr");
+  gas_assert (avr_gccisr_opcode);
+
   bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
   linkrelax = !avr_opt.no_link_relax;
 }
@@ -855,7 +1023,8 @@ static unsigned int
 avr_operand (struct avr_opcodes_s *opcode,
 	     int where,
 	     const char *op,
-	     char **line)
+	     char **line,
+	     int *pregno)
 {
   expressionS op_expr;
   unsigned int op_mask = 0;
@@ -904,6 +1073,9 @@ avr_operand (struct avr_opcodes_s *opcode,
           }
       }
 
+      if (pregno)
+	*pregno = op_mask;
+
       if (avr_mcu->mach == bfd_mach_avrtiny)
         {
           if (op_mask < 16 || op_mask > 31)
@@ -1079,6 +1251,16 @@ avr_operand (struct avr_opcodes_s *opcode,
       }
       break;
 
+    case 'N':
+      {
+	unsigned int x;
+
+	x = avr_get_constant (str, 255);
+	str = input_line_pointer;
+	op_mask = x;
+      }
+      break;
+
     case 'K':
       input_line_pointer = str;
       avr_offset_expression (& op_expr);
@@ -1145,6 +1327,8 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
   char *str = *line;
   int where = frag - frag_now->fr_literal;
   static unsigned int prev = 0;  /* Previous opcode.  */
+  int regno1 = -2;
+  int regno2 = -2;
 
   /* Opcode have operands.  */
   if (*op)
@@ -1157,7 +1341,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
       /* Parse first operand.  */
       if (REGISTER_P (*op))
 	reg1_present = 1;
-      reg1 = avr_operand (opcode, where, op, &str);
+      reg1 = avr_operand (opcode, where, op, &str, &regno1);
       ++op;
 
       /* Parse second operand.  */
@@ -1170,6 +1354,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
 	    {
 	      reg2 = reg1;
 	      reg2_present = 1;
+	      regno2 = regno1;
 	    }
 	  else
 	    {
@@ -1181,7 +1366,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
 		as_bad (_("`,' required"));
 	      str = skip_space (str);
 
-	      reg2 = avr_operand (opcode, where, op, &str);
+	      reg2 = avr_operand (opcode, where, op, &str, &regno2);
 	    }
 
 	  if (reg1_present && reg2_present)
@@ -1194,6 +1379,9 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
       bin |= reg1 | reg2;
     }
 
+  if (avr_opt.have_gccisr)
+    avr_update_gccisr (opcode, regno1, regno2);
+
   /* Detect undefined combinations (like ld r31,Z+).  */
   if (!avr_opt.all_opcodes && AVR_UNDEF_P (bin))
     as_warn (_("undefined combination of operands"));
@@ -1698,6 +1886,13 @@ md_assemble (char *str)
       return;
     }
 
+    if (opcode == avr_gccisr_opcode
+	&& !avr_opt.have_gccisr)
+    {
+      as_bad (_("pseudo instruction `%s' not supported"), op);
+      return;
+    }
+
   /* Special case for opcodes with optional operands (lpm, elpm) -
      version with operands exists in avr_opcodes[] in the next entry.  */
 
@@ -1711,7 +1906,10 @@ md_assemble (char *str)
   {
     char *t = input_line_pointer;
 
-    avr_operands (opcode, &str);
+    if (opcode == avr_gccisr_opcode)
+      avr_gccisr_operands (opcode, &str);
+    else
+      avr_operands (opcode, &str);
     if (*skip_space (str))
       as_bad (_("garbage at end of line"));
     input_line_pointer = t;
@@ -2211,3 +2409,385 @@ avr_post_relax_hook (void)
 {
   avr_create_and_fill_property_section ();
 }
+
+
+/* Accumulate information about instruction sequence to `avr_isr':
+   wheter TMP_REG, ZERO_REG and SREG might be touched.  Used during parse.
+   REG1 is either -1 or a register number used by the instruction as input
+   or output operand.  Similar for REG2.  */
+
+static void
+avr_update_gccisr (struct avr_opcodes_s *opcode, int reg1, int reg2)
+{
+  const int tiny_p = avr_mcu->mach == bfd_mach_avrtiny;
+  const int reg_tmp = tiny_p ? 16 : 0;
+  const int reg_zero = 1 + reg_tmp;
+
+  if (ISR_CHUNK_Done == avr_isr.prev_chunk
+      || (avr_isr.need_sreg
+	  && avr_isr.need_reg_tmp
+	  && avr_isr.need_reg_zero))
+    {
+      /* Nothing (more) to do */
+      return;
+    }
+
+  /* SREG: Look up instructions that don't clobber SREG.  */
+
+  if (!avr_isr.need_sreg
+      && !hash_find (avr_no_sreg_hash, opcode->name))
+    {
+      avr_isr.need_sreg = 1;
+    }
+
+  /* Handle explicit register operands.  Record *any* use as clobber.
+     This is because TMP_REG and ZERO_REG are not global and using
+     them makes no sense without a previous set.  */
+
+  avr_isr.need_reg_tmp  |= reg1 == reg_tmp  || reg2 == reg_tmp;
+  avr_isr.need_reg_zero |= reg1 == reg_zero || reg2 == reg_zero;
+
+  /* Handle implicit register operands and some opaque stuff.  */
+
+  if (strstr (opcode->name, "lpm")
+      && '?' == *opcode->constraints)
+    {
+      avr_isr.need_reg_tmp = 1;
+    }
+
+  if (strstr (opcode->name, "call")
+      || strstr (opcode->name, "mul")
+      || 0 == strcmp (opcode->name, "des")
+      || (0 == strcmp (opcode->name, "movw")
+	  && (reg1 == reg_tmp || reg2 == reg_tmp)))
+    {
+      avr_isr.need_reg_tmp = 1;
+      avr_isr.need_reg_zero = 1;
+    }
+}
+
+
+/* Emit some 1-word instruction to **PWHERE and advance *PWHERE by the number
+   of octets written.  INSN specifies the desired instruction and REG is the
+   register used by it.  This function is only used with restricted subset of
+   instructions as might be emit by `__gcc_isr'.  IN / OUT will use SREG
+   and LDI loads 0.  */
+
+static void
+avr_emit_insn (const char *insn, int reg, char **pwhere)
+{
+  const int sreg = 0x3f;
+  unsigned bin = 0;
+  const struct avr_opcodes_s *op
+    = (struct avr_opcodes_s*) hash_find (avr_hash, insn);
+
+  /* We only have to deal with: IN, OUT, PUSH, POP, CLR, LDI 0.  All of
+     these deal with at least one Reg and are 1-word instructions.  */
+
+  gas_assert (op && 1 == op->insn_size);
+  gas_assert (reg >= 0 && reg <= 31);
+
+  if (strchr (op->constraints, 'r'))
+    {
+      bin = op->bin_opcode | (reg << 4);
+    }
+  else if (strchr (op->constraints, 'd'))
+    {
+      gas_assert (reg >= 16);
+      bin = op->bin_opcode | ((reg & 0xf) << 4);
+    }
+  else
+    abort();
+
+  if (strchr (op->constraints, 'P'))
+    {
+      bin |= ((sreg & 0x30) << 5) | (sreg & 0x0f);
+    }
+  else if (0 == strcmp ("r=r", op->constraints))
+    {
+      bin |= ((reg & 0x10) << 5) | (reg & 0x0f);
+    }
+  else
+    gas_assert (0 == strcmp ("r", op->constraints)
+		|| 0 == strcmp ("ldi", op->name));
+
+  bfd_putl16 ((bfd_vma) bin, *pwhere);
+  (*pwhere) += 2 * op->insn_size;
+}
+
+
+/* Turn rs_machine_dependent frag *FR into an ordinary rs_fill code frag,
+   using information gathered in `avr_isr'.  REG is the register number as
+   supplied by Done chunk "__gcc_isr 0,REG".  */
+
+static void
+avr_patch_gccisr_frag (fragS *fr, int reg)
+{
+  int treg;
+  int n_pushed = 0;
+  char *where = fr->fr_literal;
+  const int tiny_p = avr_mcu->mach == bfd_mach_avrtiny;
+  const int reg_tmp = tiny_p ? 16 : 0;
+  const int reg_zero = 1 + reg_tmp;
+
+  /* Clearing ZERO_REG on non-Tiny needs CLR which clobbers SREG.  */
+
+  avr_isr.need_sreg |= !tiny_p && avr_isr.need_reg_zero;
+
+  /* A working register to PUSH / POP the SREG.  We might use the register
+     as supplied by ISR_CHUNK_Done for that purpose as GCC wants to push
+     it anyways.  If GCC passes ZERO_REG or TMP_REG, it has no clue (and
+     no additional regs to safe) and we use that reg.  */
+
+  treg
+    = avr_isr.need_reg_tmp   ? reg_tmp
+    : avr_isr.need_reg_zero  ? reg_zero
+    : avr_isr.need_sreg      ? reg
+    : reg > reg_zero         ? reg
+    : -1;
+
+  if (treg >= 0)
+    {
+      /* Non-empty prologue / epilogue */
+
+      if (ISR_CHUNK_Prologue == fr->fr_subtype)
+	{
+	  avr_emit_insn ("push", treg, &where);
+	  n_pushed++;
+
+	  if (avr_isr.need_sreg)
+	    {
+	      avr_emit_insn ("in",   treg, &where);
+	      avr_emit_insn ("push", treg, &where);
+	      n_pushed++;
+	    }
+
+	  if (avr_isr.need_reg_zero)
+	    {
+	      if (reg_zero != treg)
+		{
+		  avr_emit_insn ("push", reg_zero, &where);
+		  n_pushed++;
+		}
+	      avr_emit_insn (tiny_p ? "ldi" : "clr", reg_zero, &where);
+	    }
+
+	  if (reg > reg_zero && reg != treg)
+	    {
+	      avr_emit_insn ("push", reg, &where);
+	      n_pushed++;
+	    }
+	}
+      else if (ISR_CHUNK_Epilogue == fr->fr_subtype)
+	{
+	  /* Same logic as in Prologue but in reverse order and with counter
+	     parts of either instruction:  POP instead of PUSH and OUT instead
+	     of IN.  Clearing ZERO_REG has no couter part.  */
+
+	  if (reg > reg_zero && reg != treg)
+	    avr_emit_insn ("pop", reg, &where);
+
+	  if (avr_isr.need_reg_zero
+	      && reg_zero != treg)
+	    avr_emit_insn ("pop", reg_zero, &where);
+
+	  if (avr_isr.need_sreg)
+	    {
+	      avr_emit_insn ("pop", treg, &where);
+	      avr_emit_insn ("out", treg, &where);
+	    }
+
+	  avr_emit_insn ("pop", treg, &where);
+	}
+      else
+	abort();
+    } /* treg >= 0 */
+
+  if (ISR_CHUNK_Prologue == fr->fr_subtype
+      && avr_isr.sym_n_pushed)
+    {
+      symbolS *sy = avr_isr.sym_n_pushed;
+      /* Turn magic `__gcc_isr.n_pushed' into its now known value.  */
+
+      sy->sy_value.X_op = O_constant;
+      sy->sy_value.X_add_number = n_pushed;
+      S_SET_SEGMENT (sy, expr_section);
+      avr_isr.sym_n_pushed = NULL;
+    }
+
+  /* Turn frag into ordinary code frag of now known size.  */
+
+  fr->fr_var = 0;
+  fr->fr_fix = (offsetT) (where - fr->fr_literal);
+  gas_assert (fr->fr_fix <= fr->fr_offset);
+  fr->fr_offset = 0;
+  fr->fr_type = rs_fill;
+  fr->fr_subtype = 0;
+}
+
+
+/* Implements `__gcc_isr' pseudo-instruction.  For Prologue and Epilogue
+   chunks, emit a new rs_machine_dependent frag.  For Done chunks, traverse
+   the current segment and patch all rs_machine_dependent frags to become
+   appropriate rs_fill code frags.  If chunks are seen in an odd ordering,
+   throw an error instead.  */
+
+static void
+avr_gccisr_operands (struct avr_opcodes_s *opcode, char **line)
+{
+  int bad = 0;
+  int chunk, reg = 0;
+  char *str = *line;
+
+  gas_assert (avr_opt.have_gccisr);
+
+  /* We only use operands "N" and "r" which don't pop new fix-ups.  */
+
+  /* 1st operand: Which chunk of __gcc_isr: 0...2.  */
+
+  chunk = avr_operand (opcode, -1, "N", &str, NULL);
+  if (chunk < 0 || chunk > 2)
+    as_bad (_("%s requires value 0-2 as operand 1"), opcode->name);
+
+  if (ISR_CHUNK_Done == chunk)
+    {
+      /* 2nd operand: A register to push / pop.  */
+
+      str = skip_space (str);
+      if (*str == '\0' || *str++ != ',')
+	as_bad (_("`,' required"));
+      else
+	avr_operand (opcode, -1, "r", &str, &reg);
+    }
+
+  *line = str;
+
+  /* Chunks must follow in a specific order:
+     - Prologue: Exactly one
+     - Epilogue: Any number
+     - Done: Exactly one.  */
+  bad |= ISR_CHUNK_Prologue == chunk && avr_isr.prev_chunk != ISR_CHUNK_Done;
+  bad |= ISR_CHUNK_Epilogue == chunk && avr_isr.prev_chunk == ISR_CHUNK_Done;
+  bad |= ISR_CHUNK_Done == chunk && avr_isr.prev_chunk == ISR_CHUNK_Done;
+  if (bad)
+    {
+      if (avr_isr.file)
+	as_bad (_("`%s %d' after `%s %d' from %s:%u"), opcode->name, chunk,
+		opcode->name, avr_isr.prev_chunk, avr_isr.file, avr_isr.line);
+      else
+	as_bad (_("`%s %d' but no chunk open yet"), opcode->name, chunk);
+    }
+
+  if (!had_errors())
+    {
+      /* The longest sequence (prologue) might have up to 6 insns (words):
+
+	 push  R0
+	 in    R0, SREG
+	 push  R0
+	 push  R1
+	 clr   R1
+	 push  Rx
+      */
+      unsigned int size = 2 * 6;
+      fragS *fr;
+
+      switch (chunk)
+	{
+	case ISR_CHUNK_Prologue:
+	  avr_isr.need_reg_tmp = 0;
+	  avr_isr.need_reg_zero = 0;
+	  avr_isr.need_sreg = 0;
+	  avr_isr.sym_n_pushed = NULL;
+	  /* FALLTHRU */
+
+	case ISR_CHUNK_Epilogue:
+	  /* Emit a new rs_machine_dependent fragment into the fragment chain.
+	     It will be patched and cleaned up once we see the matching
+	     ISR_CHUNK_Done.  */
+	  frag_wane (frag_now);
+	  frag_new (0);
+	  frag_more (size);
+
+	  frag_now->fr_var = 1;
+	  frag_now->fr_offset = size;
+	  frag_now->fr_fix = 0;
+	  frag_now->fr_type = rs_machine_dependent;
+	  frag_now->fr_subtype = chunk;
+	  frag_new (size);
+	  break;
+
+	case ISR_CHUNK_Done:
+	  /* Traverse all frags of the current subseg and turn ones of type
+	     rs_machine_dependent into ordinary code as expected by GCC.  */
+
+	  for (fr = frchain_now->frch_root; fr; fr = fr->fr_next)
+	    if (fr->fr_type == rs_machine_dependent)
+	      avr_patch_gccisr_frag (fr, reg);
+	  break;
+
+	default:
+	  abort();
+	  break;
+	}
+    } /* !had_errors */
+
+  avr_isr.prev_chunk = chunk;
+  avr_isr.file = as_where (&avr_isr.line);
+}
+
+
+/* Callback used by the function below.  Diagnose any dangling stuff from
+   `__gcc_isr', i.e. frags of type rs_machine_dependent.  Such frags should
+   have been resolved during parse by ISR_CHUNK_Done.  If such a frag is
+   seen, report an error and turn it into something harmless.  */
+
+static void
+avr_check_gccisr_done (bfd *abfd ATTRIBUTE_UNUSED,
+		       segT section,
+		       void *xxx ATTRIBUTE_UNUSED)
+{
+  segment_info_type *info = seg_info (section);
+
+  if (SEG_NORMAL (section)
+      /* BFD may have introduced its own sections without using
+	 subseg_new, so it is possible that seg_info is NULL.  */
+      && info)
+    {
+      fragS *fr;
+      frchainS *frch;
+
+      for (frch = info->frchainP; frch; frch = frch->frch_next)
+	for (fr = frch->frch_root; fr; fr = fr->fr_next)
+	  if (fr->fr_type == rs_machine_dependent)
+	    {
+	      if (avr_isr.file)
+		as_bad_where (avr_isr.file, avr_isr.line,
+			      _("dangling `__gcc_isr %d'"), avr_isr.prev_chunk);
+	      else if (!had_errors())
+		as_bad (_("dangling `__gcc_isr'"));
+
+	      avr_isr.file = NULL;
+
+	      /* Avoid Internal errors due to rs_machine_dependent in the
+		 remainder:  Turn frag into something harmless.   */
+	      fr->fr_var = 0;
+	      fr->fr_fix = 0;
+	      fr->fr_offset = 0;
+	      fr->fr_type = rs_fill;
+	      fr->fr_subtype = 0;
+	    }
+    }
+}
+
+
+/* Implement `md_pre_output_hook' */
+/* Run over all relevant sections and diagnose any dangling `__gcc_isr'.
+   This runs after parsing all inputs but before relaxing and writing.  */
+
+void
+avr_pre_output_hook (void)
+{
+  if (avr_opt.have_gccisr)
+    bfd_map_over_sections (stdoutput, avr_check_gccisr_done, NULL);
+}
diff --git a/gas/config/tc-avr.h b/gas/config/tc-avr.h
index 399656f..0cfe9ff 100644
--- a/gas/config/tc-avr.h
+++ b/gas/config/tc-avr.h
@@ -220,6 +220,12 @@ extern bfd_boolean avr_allow_local_subtract (expressionS *, expressionS *, segT)
 #define elf_tc_final_processing 	avr_elf_final_processing
 extern void avr_elf_final_processing (void);
 
+#define md_pre_output_hook avr_pre_output_hook ()
+extern void avr_pre_output_hook (void);
+
+#define md_undefined_symbol avr_undefined_symbol
+extern symbolS* avr_undefined_symbol (char*);
+
 #define md_post_relax_hook avr_post_relax_hook ()
 extern void avr_post_relax_hook (void);
 
diff --git a/gas/doc/c-avr.texi b/gas/doc/c-avr.texi
index f829e79..e419964 100644
--- a/gas/doc/c-avr.texi
+++ b/gas/doc/c-avr.texi
@@ -18,6 +18,7 @@
 * AVR Options::              Options
 * AVR Syntax::               Syntax
 * AVR Opcodes::              Opcodes
+* AVR Pseudo Instructions::  Pseudo Instructions
 @end menu
 
 @node AVR Options
@@ -151,6 +152,10 @@ Disable support for link-time relaxation.  The assembler will resolve
 relocations when it can, and may be able to better compress some debug
 information.
 
+@cindex @code{-mgcc-isr} command line option, AVR
+@item -mgcc-isr
+Enable the @code{__gcc_isr} pseudo instruction.
+
 @end table
 
 
@@ -441,3 +446,60 @@ The following table summarizes the AVR opcodes, and their arguments.
 1001010100011001   eicall
 1001010000011001   eijmp
 @end smallexample
+
+@node AVR Pseudo Instructions
+@section Pseudo Instructions
+
+The only available pseudo-instruction @code{__gcc_isr} can be activated by
+option @option{-mgcc-isr}.
+
+@table @code
+
+@item __gcc_isr 1
+Emit code chunk to be used in avr-gcc ISR prologue.
+It will expand to at most six 1-word instructions, all optional:
+push of @code{tmp_reg}, push of @code{SREG},
+push and clear of @code{zero_reg}, push of @var{Reg}.
+
+@item __gcc_isr 2
+Emit code chunk to be used in an avr-gcc ISR epilogue.
+It will expand to at most five 1-word instructions, all optional: 
+pop of @var{Reg}, pop of @code{zero_reg},
+pop of @code{SREG}, pop of @code{tmp_reg}.
+
+@item __gcc_isr 0, @var{Reg}
+Finish avr-gcc ISR function.  Scan code since the last prologue
+for usage of: @code{SREG}, @code{tmp_reg}, @code{zero_reg}.
+Prologue chunk and epilogue chunks will be replaced by appropriate code
+to save / restore @code{SREG}, @code{tmp_reg}, @code{zero_reg} and @var{Reg}.
+
+@end table
+
+Example input:
+
+@example
+__vector1:
+    __gcc_isr 1
+    lds r24, var
+    inc r24
+    sts var, r24
+    __gcc_isr 2
+    reti
+    __gcc_isr 0, r24
+@end example
+
+Example output:
+
+@example
+00000000 <__vector1>:
+   0:   8f 93           push    r24
+   2:   8f b7           in      r24, 0x3f
+   4:   8f 93           push    r24
+   6:   80 91 60 00     lds     r24, 0x0060     ; 0x800060 <var>
+   a:   83 95           inc     r24
+   c:   80 93 60 00     sts     0x0060, r24     ; 0x800060 <var>
+  10:   8f 91           pop     r24
+  12:   8f bf           out     0x3f, r24
+  14:   8f 91           pop     r24
+  16:   18 95           reti
+@end example
diff --git a/gas/testsuite/gas/avr/gccisr-01.d b/gas/testsuite/gas/avr/gccisr-01.d
new file mode 100644
index 0000000..91e1e61
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-01.d
@@ -0,0 +1,141 @@
+#name: gccisr-01: __gcc_isr pseudo instruction
+#as: -mgcc-isr -mavr4
+#objdump: -dz
+#target: avr-*-*
+
+.*: +file format elf32-avr
+
+
+Disassembly of section \.text:
+
+00000000 <__start1>:
+   0:	68 94       	set
+
+00000002 <__vec1_start>:
+   2:	0f 92       	push	r0
+   4:	0f b6       	in	r0, 0x3f	; 63
+   6:	0f 92       	push	r0
+   8:	01 30       	cpi	r16, 0x01	; 1
+   a:	0f 90       	pop	r0
+   c:	0f be       	out	0x3f, r0	; 63
+   e:	0f 90       	pop	r0
+  10:	e8 94       	clt
+
+00000012 <__data1>:
+  12:	00 e0       	ldi	r16, 0x00	; 0
+  14:	08 00       	\.word	0x0008	; \?\?\?\?
+
+00000016 <__start2>:
+  16:	68 94       	set
+
+00000018 <__vec2_start>:
+  18:	e1 e0       	ldi	r30, 0x01	; 1
+  1a:	f0 91 00 00 	lds	r31, 0x0000	; 0x800000 <__data6\+0x7fff40>
+  1e:	f0 93 00 00 	sts	0x0000, r31	; 0x800000 <__data6\+0x7fff40>
+  22:	12 01       	movw	r2, r4
+  24:	12 95       	swap	r17
+  26:	18 95       	reti
+  28:	78 10       	cpse	r7, r8
+  2a:	78 94       	sei
+  2c:	f8 94       	cli
+  2e:	af b6       	in	r10, 0x3f	; 63
+  30:	af be       	out	0x3f, r10	; 63
+  32:	18 95       	reti
+  34:	e8 94       	clt
+
+00000036 <__data2>:
+  36:	00 e0       	ldi	r16, 0x00	; 0
+  38:	0f 00       	\.word	0x000f	; \?\?\?\?
+
+0000003a <__start3>:
+  3a:	68 94       	set
+
+0000003c <__vec3_start>:
+  3c:	1f 92       	push	r1
+  3e:	1f b6       	in	r1, 0x3f	; 63
+  40:	1f 92       	push	r1
+  42:	11 24       	eor	r1, r1
+  44:	8f 93       	push	r24
+  46:	8f 91       	pop	r24
+  48:	1f 90       	pop	r1
+  4a:	1f be       	out	0x3f, r1	; 63
+  4c:	1f 90       	pop	r1
+  4e:	18 95       	reti
+  50:	8f 91       	pop	r24
+  52:	1f 90       	pop	r1
+  54:	1f be       	out	0x3f, r1	; 63
+  56:	1f 90       	pop	r1
+  58:	18 95       	reti
+  5a:	13 94       	inc	r1
+  5c:	e8 94       	clt
+
+0000005e <__data3>:
+  5e:	00 e0       	ldi	r16, 0x00	; 0
+  60:	11 00       	\.word	0x0011	; \?\?\?\?
+
+00000062 <__start4>:
+  62:	68 94       	set
+
+00000064 <__vec4_start>:
+  64:	0f 92       	push	r0
+  66:	0f b6       	in	r0, 0x3f	; 63
+  68:	0f 92       	push	r0
+  6a:	1f 92       	push	r1
+  6c:	11 24       	eor	r1, r1
+  6e:	8f 93       	push	r24
+  70:	8f 91       	pop	r24
+  72:	1f 90       	pop	r1
+  74:	0f 90       	pop	r0
+  76:	0f be       	out	0x3f, r0	; 63
+  78:	0f 90       	pop	r0
+  7a:	18 95       	reti
+  7c:	8f 91       	pop	r24
+  7e:	1f 90       	pop	r1
+  80:	0f 90       	pop	r0
+  82:	0f be       	out	0x3f, r0	; 63
+  84:	0f 90       	pop	r0
+  86:	18 95       	reti
+  88:	01 9f       	mul	r16, r17
+  8a:	e8 94       	clt
+
+0000008c <__data4>:
+  8c:	00 e0       	ldi	r16, 0x00	; 0
+  8e:	14 00       	\.word	0x0014	; \?\?\?\?
+
+00000090 <__start5>:
+  90:	68 94       	set
+
+00000092 <__vec5_start>:
+  92:	0f 92       	push	r0
+  94:	c8 95       	lpm
+  96:	0f 90       	pop	r0
+  98:	18 95       	reti
+  9a:	0f 90       	pop	r0
+  9c:	18 95       	reti
+  9e:	e8 94       	clt
+
+000000a0 <__data5>:
+  a0:	00 e0       	ldi	r16, 0x00	; 0
+  a2:	07 00       	\.word	0x0007	; \?\?\?\?
+
+000000a4 <__start6>:
+  a4:	68 94       	set
+
+000000a6 <__vec6_start>:
+  a6:	af 93       	push	r26
+  a8:	af b7       	in	r26, 0x3f	; 63
+  aa:	af 93       	push	r26
+  ac:	af 91       	pop	r26
+  ae:	af bf       	out	0x3f, r26	; 63
+  b0:	af 91       	pop	r26
+  b2:	18 95       	reti
+  b4:	af 91       	pop	r26
+  b6:	af bf       	out	0x3f, r26	; 63
+  b8:	af 91       	pop	r26
+  ba:	18 95       	reti
+  bc:	88 94       	clc
+  be:	e8 94       	clt
+
+000000c0 <__data6>:
+  c0:	00 e0       	ldi	r16, 0x00	; 0
+  c2:	0d 00       	\.word	0x000d	; \?\?\?\?
diff --git a/gas/testsuite/gas/avr/gccisr-01.s b/gas/testsuite/gas/avr/gccisr-01.s
new file mode 100644
index 0000000..82cf9f6
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-01.s
@@ -0,0 +1,127 @@
+.text
+
+;;; Use SREG
+
+__start1:
+    set
+
+__vec1_start:
+    __gcc_isr 1
+    foo = __gcc_isr.n_pushed
+    cpi r16,1
+    __gcc_isr 2
+    __gcc_isr 0,r0
+    clt
+__vec1_end:
+__data1:
+    ldi r16, foo - 2
+    .word (__vec1_end - __vec1_start) / 2
+
+;;; Nothing used.
+
+__start2:
+    set
+
+__vec2_start:
+    __gcc_isr 1
+    foo = __gcc_isr.n_pushed
+    ldi r30, 1
+    lds r31, 0
+    sts 0, r31
+    movw r2, r4
+    swap r17
+    __gcc_isr 2
+    reti
+    __gcc_isr 2
+    cpse r7, r8
+    sei
+    cli
+    in  r10, 0x3f
+    out 0x3f, r10
+    reti
+    __gcc_isr 0,r0
+    clt
+__vec2_end:
+__data2:
+    ldi r16, foo - 0
+    .word (__vec2_end - __vec2_start) / 2
+
+;;; Use SREG, ZERO and R24
+
+__start3:
+    set
+
+__vec3_start:
+    __gcc_isr 1
+    foo = __gcc_isr.n_pushed
+    __gcc_isr 2
+    reti
+    __gcc_isr 2
+    reti
+    inc r1
+    __gcc_isr 0,r24
+    clt
+__vec3_end:
+__data3:
+    ldi r16, foo - 3
+    .word (__vec3_end - __vec3_start) / 2
+
+;;; Use SREG, ZERO, TMP and R24
+
+__start4:
+    set
+
+__vec4_start:
+    __gcc_isr 1
+    foo = __gcc_isr.n_pushed
+    __gcc_isr 2
+    reti
+    __gcc_isr 2
+    reti
+    mul 16, 17
+    __gcc_isr 0,r24
+    clt
+__vec4_end:
+__data4:
+    ldi r16, foo - 4
+    .word (__vec4_end - __vec4_start) / 2
+
+;;; Use TMP
+
+__start5:
+    set
+
+__vec5_start:
+    __gcc_isr 1
+    lpm
+    foo = __gcc_isr.n_pushed
+    __gcc_isr 2
+    reti
+    __gcc_isr 2
+    reti
+    __gcc_isr 0,r0
+    clt
+__vec5_end:
+__data5:
+    ldi r16, foo - 1
+    .word (__vec5_end - __vec5_start) / 2
+
+;;; Use SREG, R26
+
+__start6:
+    set
+
+__vec6_start:
+    __gcc_isr 1
+    foo = __gcc_isr.n_pushed
+    __gcc_isr 2
+    reti
+    __gcc_isr 2
+    reti
+    clc
+    __gcc_isr 0,r26
+    clt
+__vec6_end:
+__data6:
+    ldi r16, foo - 2
+    .word (__vec6_end - __vec6_start) / 2
diff --git a/gas/testsuite/gas/avr/gccisr-02.d b/gas/testsuite/gas/avr/gccisr-02.d
new file mode 100644
index 0000000..b0724c4
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-02.d
@@ -0,0 +1,43 @@
+#name: gccisr-02: __gcc_isr pseudo instruction
+#as: -mgcc-isr -mavrtiny
+#objdump: -dz
+#target: avr-*-*
+
+.*: +file format elf32-avr
+
+
+Disassembly of section \.text:
+
+00000000 <__start1>:
+   0:	68 94       	set
+
+00000002 <__vec1_start>:
+   2:	0f 93       	push	r16
+   4:	0f b7       	in	r16, 0x3f	; 63
+   6:	0f 93       	push	r16
+   8:	21 30       	cpi	r18, 0x01	; 1
+   a:	0f 91       	pop	r16
+   c:	0f bf       	out	0x3f, r16	; 63
+   e:	0f 91       	pop	r16
+  10:	e8 94       	clt
+
+00000012 <__data1>:
+  12:	00 e0       	ldi	r16, 0x00	; 0
+  14:	08 00       	\.word	0x0008	; \?\?\?\?
+
+00000016 <__start2>:
+  16:	68 94       	set
+
+00000018 <__vec2_start>:
+  18:	1f 93       	push	r17
+  1a:	10 e0       	ldi	r17, 0x00	; 0
+  1c:	1f 91       	pop	r17
+  1e:	18 95       	reti
+  20:	e1 2f       	mov	r30, r17
+  22:	1f 91       	pop	r17
+  24:	18 95       	reti
+  26:	e8 94       	clt
+
+00000028 <__data2>:
+  28:	00 e0       	ldi	r16, 0x00	; 0
+  2a:	08 00       	\.word	0x0008	; \?\?\?\?
diff --git a/gas/testsuite/gas/avr/gccisr-02.s b/gas/testsuite/gas/avr/gccisr-02.s
new file mode 100644
index 0000000..167c42d1
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-02.s
@@ -0,0 +1,38 @@
+.text
+
+;;; Use SREG
+
+__start1:
+    set
+
+__vec1_start:
+    __gcc_isr 1
+    foo = __gcc_isr.n_pushed
+    cpi r18,1
+    __gcc_isr 2
+    __gcc_isr 0,r16
+    clt
+__vec1_end:
+__data1:
+    ldi r16, foo - 2
+    .word (__vec1_end - __vec1_start) / 2
+
+;;; Use ZERO
+
+__start2:
+    set
+
+__vec2_start:
+    __gcc_isr 1
+    foo = __gcc_isr.n_pushed
+    __gcc_isr 2
+    reti
+    mov r30,r17
+    __gcc_isr 2
+    reti
+    __gcc_isr 0,r16
+    clt
+__vec2_end:
+__data2:
+    ldi r16, foo - 1
+    .word (__vec2_end - __vec2_start) / 2
diff --git a/gas/testsuite/gas/avr/gccisr-03.d b/gas/testsuite/gas/avr/gccisr-03.d
new file mode 100644
index 0000000..0eaa28d
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-03.d
@@ -0,0 +1,4 @@
+#name: __gcc_isr pseudo instruction, test gccisr-03
+#as: 
+#error: pseudo instruction `__gcc_isr' not supported
+#target: avr-*-*
diff --git a/gas/testsuite/gas/avr/gccisr-03.s b/gas/testsuite/gas/avr/gccisr-03.s
new file mode 100644
index 0000000..39938c8
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-03.s
@@ -0,0 +1,6 @@
+.text
+
+;;; 
+
+__start1:
+    __gcc_isr 1
diff --git a/include/ChangeLog b/include/ChangeLog
index b940504..9400f16 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,8 @@
+2017-06-30  Georg-Johann Lay  <avr@gjlay.de>
+
+	PR gas/21683
+	* opcode/avr.h (AVR_INSN): Add one for __gcc_isr.
+
 2017-06-30  Maciej W. Rozycki  <macro@imgtec.com>
 	    Andrew Bennett  <andrew.bennett@imgtec.com>
 
diff --git a/include/opcode/avr.h b/include/opcode/avr.h
index 1c73022..2212816 100644
--- a/include/opcode/avr.h
+++ b/include/opcode/avr.h
@@ -110,6 +110,7 @@
    z - Z pointer register (for [e]lpm Rd,Z[+])
    M - immediate value from 0 to 255
    n - immediate value from 0 to 255 ( n = ~M ). Relocation impossible
+   N - immediate value from 0 to 255. Relocation impossible
    s - immediate value from 0 to 7
    P - Port address value from 0 to 63. (in, out)
    p - Port address value from 0 to 31. (cbi, sbi, sbic, sbis)
@@ -306,3 +307,7 @@ AVR_INSN (eijmp, "",   "1001010000011001", 1, AVR_ISA_EIND, 0x9419)
 /* DES instruction for encryption and decryption.  */
 AVR_INSN (des,  "E",   "10010100EEEE1011", 1, AVR_ISA_DES,  0x940B)
 
+/* Operands are evaluated by hand and won't pop new fux-ups.
+   The pseudo-insn is hidden behind NOP so that avr-dis.c don't see it. */
+AVR_INSN (__gcc_isr, "", "0000000000000000", 1, AVR_ISA_1200,  0x0)
+


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]