Add support for a __gcc_isr pseudo isntruction to the AVR assembler.
authorGeorg-Johann Lay <avr@gjlay.de>
Fri, 30 Jun 2017 15:37:39 +0000 (16:37 +0100)
committerNick Clifton <nickc@redhat.com>
Fri, 30 Jun 2017 15:37:39 +0000 (16:37 +0100)
    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.

12 files changed:
gas/ChangeLog
gas/config/tc-avr.c
gas/config/tc-avr.h
gas/doc/c-avr.texi
gas/testsuite/gas/avr/gccisr-01.d [new file with mode: 0644]
gas/testsuite/gas/avr/gccisr-01.s [new file with mode: 0644]
gas/testsuite/gas/avr/gccisr-02.d [new file with mode: 0644]
gas/testsuite/gas/avr/gccisr-02.s [new file with mode: 0644]
gas/testsuite/gas/avr/gccisr-03.d [new file with mode: 0644]
gas/testsuite/gas/avr/gccisr-03.s [new file with mode: 0644]
include/ChangeLog
include/opcode/avr.h

index ffc661ec5a2f0c7d2f6def346e033cf23a09ac6f..9989f313c63a338e7a9a67809717f8836481e719 100644 (file)
@@ -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.
index 79837c84fa1876e8acbef4dcb77db593bae58845..7e3f7191067f7b9216312eb4c8a18fcace8ad07f 100644 (file)
@@ -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);
+}
index 399656fa2ba0f4fbb4e2a01b93e27d1c3166b03d..0cfe9ff92a0803b32bb012e754dacc3b7d51b6f5 100644 (file)
@@ -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);
 
index f829e7968565139cb2f06365f7b1f21d6d872f2d..e419964aee71736fecdf482504294b54c9b208da 100644 (file)
@@ -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 (file)
index 0000000..91e1e61
--- /dev/null
@@ -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 (file)
index 0000000..82cf9f6
--- /dev/null
@@ -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 (file)
index 0000000..b0724c4
--- /dev/null
@@ -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 (file)
index 0000000..167c42d
--- /dev/null
@@ -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 (file)
index 0000000..0eaa28d
--- /dev/null
@@ -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 (file)
index 0000000..39938c8
--- /dev/null
@@ -0,0 +1,6 @@
+.text
+
+;;; 
+
+__start1:
+    __gcc_isr 1
index b9405040106469d56ea30cae33761e91ddd7a64b..9400f16c9a6daad55915297a9f48342884251e6c 100644 (file)
@@ -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>
 
index 1c73022ed49f53f59e516cd093f34d9e298a81ac..2212816cbf808910c36aa87c47f4f99e3280c3c5 100644 (file)
    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)
+