tc-cr16.c: Use memmove to concatenate 2 overlapping strings
[deliverable/binutils-gdb.git] / gas / config / tc-avr.c
index 999ffb487153f3572774a1d56a747f3ff33bc1d7..3e0b3102c9fecab0820817d922dba5d2dc036d18 100644 (file)
@@ -1,6 +1,6 @@
 /* tc-avr.c -- Assembler code for the ATMEL AVR
 
-   Copyright (C) 1999-2016 Free Software Foundation, Inc.
+   Copyright (C) 1999-2020 Free Software Foundation, Inc.
    Contributed by Denis Chertykov <denisc@overta.ru>
 
    This file is part of GAS, the GNU Assembler.
@@ -54,6 +54,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[] = "$";
@@ -72,19 +173,19 @@ struct mcu_type_s
 static struct mcu_type_s mcu_types[] =
 {
   {"avr1",       AVR_ISA_AVR1,    bfd_mach_avr1},
-/* TODO: insruction set for avr2 architecture should be AVR_ISA_AVR2,
+/* TODO: instruction set for avr2 architecture should be AVR_ISA_AVR2,
  but set to AVR_ISA_AVR25 for some following version
  of GCC (from 4.3) for backward compatibility.  */
   {"avr2",       AVR_ISA_AVR25,   bfd_mach_avr2},
   {"avr25",      AVR_ISA_AVR25,   bfd_mach_avr25},
-/* TODO: insruction set for avr3 architecture should be AVR_ISA_AVR3,
+/* TODO: instruction set for avr3 architecture should be AVR_ISA_AVR3,
  but set to AVR_ISA_AVR3_ALL for some following version
  of GCC (from 4.3) for backward compatibility.  */
   {"avr3",       AVR_ISA_AVR3_ALL, bfd_mach_avr3},
   {"avr31",      AVR_ISA_AVR31,   bfd_mach_avr31},
   {"avr35",      AVR_ISA_AVR35,   bfd_mach_avr35},
   {"avr4",       AVR_ISA_AVR4,    bfd_mach_avr4},
-/* TODO: insruction set for avr5 architecture should be AVR_ISA_AVR5,
+/* TODO: instruction set for avr5 architecture should be AVR_ISA_AVR5,
  but set to AVR_ISA_AVR51 for some following version
  of GCC (from 4.3) for backward compatibility.  */
   {"avr5",       AVR_ISA_AVR51,   bfd_mach_avr5},
@@ -300,6 +401,21 @@ static struct mcu_type_s mcu_types[] =
   {"atxmega16e5", AVR_ISA_XMEGA,  bfd_mach_avrxmega2},
   {"atxmega8e5",  AVR_ISA_XMEGA,  bfd_mach_avrxmega2},
   {"atxmega32x1", AVR_ISA_XMEGA,  bfd_mach_avrxmega2},
+  {"attiny212",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny214",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny412",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny414",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny416",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny417",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny814",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny816",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny817",   AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny1614",  AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny1616",  AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny1617",  AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny3214",  AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny3216",  AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
+  {"attiny3217",  AVR_ISA_XMEGA,  bfd_mach_avrxmega3},
   {"atxmega64a3", AVR_ISA_XMEGA,  bfd_mach_avrxmega4},
   {"atxmega64a3u",AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
   {"atxmega64a4u",AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
@@ -355,9 +471,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";
@@ -398,7 +515,7 @@ static struct exp_mod_s exp_mod[] =
   {"hhi8",   BFD_RELOC_AVR_MS8_LDI,    BFD_RELOC_AVR_MS8_LDI_NEG,    0},
 };
 
-/* A union used to store indicies into the exp_mod[] array
+/* A union used to store indices into the exp_mod[] array
    in a hash table which expects void * data types.  */
 typedef union
 {
@@ -412,6 +529,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
 {
@@ -420,7 +564,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[] =
@@ -432,6 +577,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 }
 };
 
@@ -525,7 +671,7 @@ md_show_usage (FILE *stream)
        "                   avr51 - enhanced AVR core with up to 128K program memory\n"
        "                   avr6  - enhanced AVR core with up to 256K program memory\n"
        "                   avrxmega2 - XMEGA, > 8K, < 64K FLASH, < 64K RAM\n"
-       "                   avrxmega3 - XMEGA, > 8K, <= 64K FLASH, > 64K RAM\n"
+       "                   avrxmega3 - XMEGA, RAM + FLASH < 64K, Flash visible in RAM\n"
        "                   avrxmega4 - XMEGA, > 64K, <= 128K FLASH, <= 64K RAM\n"
        "                   avrxmega5 - XMEGA, > 64K, <= 128K FLASH, > 64K RAM\n"
        "                   avrxmega6 - XMEGA, > 128K, <= 256K FLASH, <= 64K RAM\n"
@@ -540,6 +686,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);
 }
@@ -606,14 +753,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;
 }
 
@@ -655,6 +826,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;
 }
@@ -851,7 +1033,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;
@@ -900,6 +1083,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)
@@ -1075,6 +1261,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);
@@ -1129,6 +1325,15 @@ avr_operand (struct avr_opcodes_s *opcode,
   return op_mask;
 }
 
+/* TC_FRAG_INIT hook */
+
+void
+avr_frag_init (fragS *frag)
+{
+  memset (& frag->tc_frag_data, 0, sizeof frag->tc_frag_data);
+}
+
+
 /* Parse instruction operands.
    Return binary opcode.  */
 
@@ -1140,7 +1345,8 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
   char *frag = frag_more (opcode->insn_size * 2);
   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)
@@ -1153,7 +1359,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.  */
@@ -1166,6 +1372,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
            {
              reg2 = reg1;
              reg2_present = 1;
+             regno2 = regno1;
            }
          else
            {
@@ -1177,7 +1384,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)
@@ -1190,6 +1397,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"));
@@ -1200,7 +1410,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
          (AVR core bug, fixed in the newer devices).  */
       if (!(avr_opt.no_skip_bug ||
             (avr_mcu->isa & (AVR_ISA_MUL | AVR_ISA_MOVW)))
-         && AVR_SKIP_P (prev))
+         && AVR_SKIP_P (frag_now->tc_frag_data.prev_opcode))
        as_warn (_("skipping two-word instruction"));
 
       bfd_putl32 ((bfd_vma) bin, frag);
@@ -1208,7 +1418,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
   else
     bfd_putl16 ((bfd_vma) bin, frag);
 
-  prev = bin;
+  frag_now->tc_frag_data.prev_opcode = bin;
   *line = str;
   return bin;
 }
@@ -1219,7 +1429,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
 valueT
 md_section_align (asection *seg, valueT addr)
 {
-  int align = bfd_get_section_alignment (stdoutput, seg);
+  int align = bfd_section_alignment (seg);
   return ((addr + (1 << align) - 1) & (-1UL << align));
 }
 
@@ -1616,9 +1826,9 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED,
       return NULL;
     }
 
-  reloc = xmalloc (sizeof (arelent));
+  reloc = XNEW (arelent);
 
-  reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+  reloc->sym_ptr_ptr = XNEW (asymbol *);
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
 
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
@@ -1673,7 +1883,7 @@ md_assemble (char *str)
         specifications with same mnemonic who's ISA bits matches.
 
          This requires include/opcode/avr.h to have the instructions with
-         same mnenomic to be specified in sequence.  */
+         same mnemonic to be specified in sequence.  */
 
       while ((opcode->isa & avr_mcu->isa) != opcode->isa)
         {
@@ -1694,6 +1904,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.  */
 
@@ -1707,7 +1924,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;
@@ -1731,8 +1951,6 @@ const exp_mod_data_t exp_mod_data[] =
   { "hi8",  1, BFD_RELOC_AVR_8_HI,  "`hi8' "  },
   { "hlo8", 1, BFD_RELOC_AVR_8_HLO, "`hlo8' " },
   { "hh8",  1, BFD_RELOC_AVR_8_HLO, "`hh8' "  },
-  /* End of list.  */
-  { NULL, 0, 0, NULL }
 };
 
 /* Parse special CONS expression: pm (expression) or alternatively
@@ -1742,16 +1960,17 @@ const exp_mod_data_t exp_mod_data[] =
 const exp_mod_data_t *
 avr_parse_cons_expression (expressionS *exp, int nbytes)
 {
-  const exp_mod_data_t *pexp = &exp_mod_data[0];
   char *tmp;
+  unsigned int i;
 
   tmp = input_line_pointer = skip_space (input_line_pointer);
 
   /* The first entry of exp_mod_data[] contains an entry if no
      expression modifier is present.  Skip it.  */
 
-  for (pexp++; pexp->name; pexp++)
+  for (i = 0; i < ARRAY_SIZE (exp_mod_data); i++)
     {
+      const exp_mod_data_t *pexp = &exp_mod_data[i];
       int len = strlen (pexp->name);
 
       if (nbytes == pexp->nbytes
@@ -1820,7 +2039,7 @@ avr_cons_fix_new (fragS *frag,
     }
 
   if (bad)
-    as_bad (_("illegal %srelocation size: %d"), pexp_mod_data->error, nbytes);
+    as_bad (_("illegal %s relocation size: %d"), pexp_mod_data->error, nbytes);
 }
 
 static bfd_boolean
@@ -1844,7 +2063,7 @@ tc_cfi_frame_initial_instructions (void)
   cfi_add_CFA_def_cfa (32, return_size);
 
   /* Note that AVR consistently uses post-decrement, which means that things
-     do not line up the same way as for targers that use pre-decrement.  */
+     do not line up the same way as for targets that use pre-decrement.  */
   cfi_add_CFA_offset (DWARF2_DEFAULT_RETURN_COLUMN, 1-return_size);
 }
 
@@ -2010,7 +2229,7 @@ avr_create_property_section (void)
   sec = bfd_make_section (stdoutput, section_name);
   if (sec == NULL)
     as_fatal (_("Failed to create property section `%s'\n"), section_name);
-  bfd_set_section_flags (stdoutput, sec, flags);
+  bfd_set_section_flags (sec, flags);
   sec->output_section = sec;
   return sec;
 }
@@ -2069,8 +2288,7 @@ create_record_for_frag (segT sec, fragS *fragP)
 {
   struct avr_property_record_link *prop_rec_link;
 
-  prop_rec_link = xmalloc (sizeof (struct avr_property_record_link));
-  memset (prop_rec_link, 0, sizeof (*prop_rec_link));
+  prop_rec_link = XCNEW (struct avr_property_record_link);
   gas_assert (fragP->fr_next != NULL);
 
   if (fragP->tc_frag_data.is_org)
@@ -2186,7 +2404,7 @@ avr_create_and_fill_property_section (void)
     return;
 
   prop_sec = avr_create_property_section ();
-  bfd_set_section_size (stdoutput, prop_sec, sec_size);
+  bfd_set_section_size (prop_sec, sec_size);
 
   subseg_set (prop_sec, 0);
   frag_base = frag_more (sec_size);
@@ -2209,3 +2427,384 @@ 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.  */
+
+      S_SET_VALUE (sy, 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 = where - fr->fr_literal;
+  gas_assert (fr->fr_fix <= (valueT) 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);
+}
This page took 0.034653 seconds and 4 git commands to generate.