ubsan: z8k: index 10 out of bounds for type 'unsigned int const[10]'
[deliverable/binutils-gdb.git] / gas / config / tc-avr.c
index 6b4d46fb7796e4ff747fe4f825a7e3b207d22ae7..3e0b3102c9fecab0820817d922dba5d2dc036d18 100644 (file)
@@ -1,14 +1,13 @@
 /* tc-avr.c -- Assembler code for the ATMEL AVR
 
 /* tc-avr.c -- Assembler code for the ATMEL AVR
 
-   Copyright 1999, 2000, 2001, 2002, 2004, 2005, 2006
-   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.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    Contributed by Denis Chertykov <denisc@overta.ru>
 
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
    any later version.
 
    GAS is distributed in the hope that it will be useful,
 #include "as.h"
 #include "safe-ctype.h"
 #include "subsegs.h"
 #include "as.h"
 #include "safe-ctype.h"
 #include "subsegs.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include "elf/avr.h"
+#include "elf32-avr.h"
+
+/* For building a linked list of AVR_PROPERTY_RECORD structures.  */
+struct avr_property_record_link
+{
+  struct avr_property_record record;
+  struct avr_property_record_link *next;
+};
 
 struct avr_opcodes_s
 {
 
 struct avr_opcodes_s
 {
-  char *        name;
-  char *        constraints;
+  const char *        name;
+  const char *        constraints;
+  const char *        opcode;
   int           insn_size;             /* In words.  */
   int           isa;
   unsigned int  bin_opcode;
 };
 
 #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
   int           insn_size;             /* In words.  */
   int           isa;
   unsigned int  bin_opcode;
 };
 
 #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
-{#NAME, CONSTR, SIZE, ISA, BIN},
+{#NAME, CONSTR, OPCODE, SIZE, ISA, BIN},
 
 struct avr_opcodes_s avr_opcodes[] =
 {
   #include "opcode/avr.h"
 
 struct avr_opcodes_s avr_opcodes[] =
 {
   #include "opcode/avr.h"
-  {NULL, NULL, 0, 0, 0}
+  {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[] = "$";
 const char comment_chars[] = ";";
 const char line_comment_chars[] = "#";
 const char line_separator_chars[] = "$";
@@ -50,118 +162,305 @@ const char line_separator_chars[] = "$";
 const char *md_shortopts = "m:";
 struct mcu_type_s
 {
 const char *md_shortopts = "m:";
 struct mcu_type_s
 {
-  char *name;
+  const char *name;
   int isa;
   int mach;
 };
 
 /* XXX - devices that don't seem to exist (renamed, replaced with larger
   int isa;
   int mach;
 };
 
 /* XXX - devices that don't seem to exist (renamed, replaced with larger
-   ones, or planned but never produced), left here for compatibility.
-   TODO: hide them in show_mcu_list output?  */
+   ones, or planned but never produced), left here for compatibility.  */
 
 static struct mcu_type_s mcu_types[] =
 {
 
 static struct mcu_type_s mcu_types[] =
 {
-  {"avr1",       AVR_ISA_TINY1,   bfd_mach_avr1},
-  {"avr2",       AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"avr3",       AVR_ISA_M103,    bfd_mach_avr3},
-  {"avr4",       AVR_ISA_M8,      bfd_mach_avr4},
-  {"avr5",       AVR_ISA_ALL,     bfd_mach_avr5},
-  {"avr6",       AVR_ISA_ALL,     bfd_mach_avr6},
+  {"avr1",       AVR_ISA_AVR1,    bfd_mach_avr1},
+/* 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: 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: 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},
+  {"avr51",      AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"avr6",       AVR_ISA_AVR6,    bfd_mach_avr6},
+  {"avrxmega1",  AVR_ISA_XMEGA,   bfd_mach_avrxmega1},
+  {"avrxmega2",  AVR_ISA_XMEGA,   bfd_mach_avrxmega2},
+  {"avrxmega3",  AVR_ISA_XMEGA,   bfd_mach_avrxmega3},
+  {"avrxmega4",  AVR_ISA_XMEGA,   bfd_mach_avrxmega4},
+  {"avrxmega5",  AVR_ISA_XMEGA,   bfd_mach_avrxmega5},
+  {"avrxmega6",  AVR_ISA_XMEGA,   bfd_mach_avrxmega6},
+  {"avrxmega7",  AVR_ISA_XMEGA,   bfd_mach_avrxmega7},
+  {"avrtiny",    AVR_ISA_AVRTINY, bfd_mach_avrtiny},
   {"at90s1200",  AVR_ISA_1200,    bfd_mach_avr1},
   {"at90s1200",  AVR_ISA_1200,    bfd_mach_avr1},
-  {"attiny10",   AVR_ISA_TINY1,   bfd_mach_avr1}, /* XXX -> tn11 */
-  {"attiny11",   AVR_ISA_TINY1,   bfd_mach_avr1},
-  {"attiny12",   AVR_ISA_TINY1,   bfd_mach_avr1},
-  {"attiny15",   AVR_ISA_TINY1,   bfd_mach_avr1},
-  {"attiny28",   AVR_ISA_TINY1,   bfd_mach_avr1},
-  {"at90s2313",  AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"at90s2323",  AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"at90s2333",  AVR_ISA_2xxx,    bfd_mach_avr2}, /* XXX -> 4433 */
-  {"at90s2343",  AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"attiny22",   AVR_ISA_2xxx,    bfd_mach_avr2}, /* XXX -> 2343 */
-  {"attiny26",   AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"at90s4433",  AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"at90s4414",  AVR_ISA_2xxx,    bfd_mach_avr2}, /* XXX -> 8515 */
-  {"at90s4434",  AVR_ISA_2xxx,    bfd_mach_avr2}, /* XXX -> 8535 */
-  {"at90s8515",  AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"at90s8535",  AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"at90c8534",  AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"at86rf401",  AVR_ISA_2xxx,    bfd_mach_avr2},
-  {"attiny13",   AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny2313", AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny261",  AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny461",  AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny861",  AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny24",   AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny44",   AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny84",   AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny25",   AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny45",   AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"attiny85",   AVR_ISA_TINY2,   bfd_mach_avr2},
-  {"atmega603",  AVR_ISA_M603,    bfd_mach_avr3}, /* XXX -> m103 */
-  {"atmega103",  AVR_ISA_M103,    bfd_mach_avr3},
-  {"at43usb320", AVR_ISA_M103,    bfd_mach_avr3},
-  {"at43usb355", AVR_ISA_M603,    bfd_mach_avr3},
-  {"at76c711",   AVR_ISA_M603,    bfd_mach_avr3},
-  {"atmega48",   AVR_ISA_PWMx,    bfd_mach_avr4},
+  {"attiny11",   AVR_ISA_AVR1,    bfd_mach_avr1},
+  {"attiny12",   AVR_ISA_AVR1,    bfd_mach_avr1},
+  {"attiny15",   AVR_ISA_AVR1,    bfd_mach_avr1},
+  {"attiny28",   AVR_ISA_AVR1,    bfd_mach_avr1},
+  {"at90s2313",  AVR_ISA_AVR2,    bfd_mach_avr2},
+  {"at90s2323",  AVR_ISA_AVR2,    bfd_mach_avr2},
+  {"at90s2333",  AVR_ISA_AVR2,    bfd_mach_avr2}, /* XXX -> 4433 */
+  {"at90s2343",  AVR_ISA_AVR2,    bfd_mach_avr2},
+  {"attiny22",   AVR_ISA_AVR2,    bfd_mach_avr2}, /* XXX -> 2343 */
+  {"attiny26",   AVR_ISA_2xxe,    bfd_mach_avr2},
+  {"at90s4414",  AVR_ISA_AVR2,    bfd_mach_avr2}, /* XXX -> 8515 */
+  {"at90s4433",  AVR_ISA_AVR2,    bfd_mach_avr2},
+  {"at90s4434",  AVR_ISA_AVR2,    bfd_mach_avr2}, /* XXX -> 8535 */
+  {"at90s8515",  AVR_ISA_AVR2,    bfd_mach_avr2},
+  {"at90c8534",  AVR_ISA_AVR2,    bfd_mach_avr2},
+  {"at90s8535",  AVR_ISA_AVR2,    bfd_mach_avr2},
+  {"ata5272",    AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny13",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny13a",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny2313", AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny2313a",AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny24",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny24a",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny4313", AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny44",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny44a",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny84",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny84a",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny25",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny45",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny85",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny261",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny261a", AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny461",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny461a", AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny861",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny861a", AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny87",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny43u",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny48",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny88",   AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"attiny828",  AVR_ISA_AVR25,   bfd_mach_avr25},
+  {"at86rf401",  AVR_ISA_RF401,   bfd_mach_avr25},
+  {"at43usb355", AVR_ISA_AVR3,    bfd_mach_avr3},
+  {"at76c711",   AVR_ISA_AVR3,    bfd_mach_avr3},
+  {"atmega103",  AVR_ISA_AVR31,   bfd_mach_avr31},
+  {"at43usb320", AVR_ISA_AVR31,   bfd_mach_avr31},
+  {"attiny167",  AVR_ISA_AVR35,   bfd_mach_avr35},
+  {"at90usb82",  AVR_ISA_AVR35,   bfd_mach_avr35},
+  {"at90usb162", AVR_ISA_AVR35,   bfd_mach_avr35},
+  {"ata5505",    AVR_ISA_AVR35,   bfd_mach_avr35},
+  {"atmega8u2",  AVR_ISA_AVR35,   bfd_mach_avr35},
+  {"atmega16u2", AVR_ISA_AVR35,   bfd_mach_avr35},
+  {"atmega32u2", AVR_ISA_AVR35,   bfd_mach_avr35},
+  {"attiny1634", AVR_ISA_AVR35,   bfd_mach_avr35},
   {"atmega8",    AVR_ISA_M8,      bfd_mach_avr4},
   {"atmega8",    AVR_ISA_M8,      bfd_mach_avr4},
-  {"atmega83",   AVR_ISA_M8,      bfd_mach_avr4}, /* XXX -> m8535 */
-  {"atmega85",   AVR_ISA_M8,      bfd_mach_avr4}, /* XXX -> m8 */
-  {"atmega88",   AVR_ISA_PWMx,    bfd_mach_avr4},
+  {"ata6289",    AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega8a",   AVR_ISA_M8,      bfd_mach_avr4},
+  {"ata6285",    AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"ata6286",    AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega48",   AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega48a",  AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega48pa", AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega48p",  AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega88",   AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega88a",  AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega88p",  AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"atmega88pa", AVR_ISA_AVR4,    bfd_mach_avr4},
   {"atmega8515", AVR_ISA_M8,      bfd_mach_avr4},
   {"atmega8535", AVR_ISA_M8,      bfd_mach_avr4},
   {"atmega8515", AVR_ISA_M8,      bfd_mach_avr4},
   {"atmega8535", AVR_ISA_M8,      bfd_mach_avr4},
-  {"at90pwm1",   AVR_ISA_PWMx,    bfd_mach_avr4},
-  {"at90pwm2",   AVR_ISA_PWMx,    bfd_mach_avr4},
-  {"at90pwm3",   AVR_ISA_PWMx,    bfd_mach_avr4},
-  {"at90usb82",  AVR_ISA_PWMx,    bfd_mach_avr4},
-  {"atmega16",   AVR_ISA_M323,    bfd_mach_avr5},
+  {"atmega8hva", AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"at90pwm1",   AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"at90pwm2",   AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"at90pwm2b",  AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"at90pwm3",   AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"at90pwm3b",  AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"at90pwm81",  AVR_ISA_AVR4,    bfd_mach_avr4},
+  {"at90pwm161", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"ata5790",    AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"ata5795",    AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega16",   AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega16a",  AVR_ISA_AVR5,    bfd_mach_avr5},
   {"atmega161",  AVR_ISA_M161,    bfd_mach_avr5},
   {"atmega161",  AVR_ISA_M161,    bfd_mach_avr5},
-  {"atmega162",  AVR_ISA_M323,    bfd_mach_avr5},
+  {"atmega162",  AVR_ISA_AVR5,    bfd_mach_avr5},
   {"atmega163",  AVR_ISA_M161,    bfd_mach_avr5},
   {"atmega163",  AVR_ISA_M161,    bfd_mach_avr5},
-  {"atmega164p", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega165",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega165p", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega168",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega169",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega169p", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega32",   AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega323",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega324p", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega325",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega325p", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega329",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega329p", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega3250", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega3250p",AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega3290", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega3290p",AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega406",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega64",   AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega640",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega644",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega644p", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega128",  AVR_ISA_M128,    bfd_mach_avr5},
-  {"atmega1280", AVR_ISA_M128,    bfd_mach_avr5},
-  {"atmega1281", AVR_ISA_M128,    bfd_mach_avr5},
-  {"atmega645",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega649",  AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega6450", AVR_ISA_M323,    bfd_mach_avr5},
-  {"atmega6490", AVR_ISA_M323,    bfd_mach_avr5},
-  {"at90can32" , AVR_ISA_M323,    bfd_mach_avr5},
-  {"at90can64" , AVR_ISA_M323,    bfd_mach_avr5},
-  {"at90can128", AVR_ISA_M128,    bfd_mach_avr5},
-  {"at90usb162", AVR_ISA_M323,    bfd_mach_avr5},
-  {"at90usb646", AVR_ISA_M323,    bfd_mach_avr5},
-  {"at90usb647", AVR_ISA_M323,    bfd_mach_avr5},
-  {"at90usb1286",AVR_ISA_M128,    bfd_mach_avr5},
-  {"at90usb1287",AVR_ISA_M128,    bfd_mach_avr5},
+  {"atmega164a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega164p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega164pa",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega165",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega165a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega165p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega165pa",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega168",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega168a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega168p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega168pa",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega169",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega169a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega169p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega169pa",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega32",   AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega32a",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega323",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega324a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega324p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega324pa",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega325",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega325a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega325p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega325pa",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega3250", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega3250a",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega3250p",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega3250pa",AVR_ISA_AVR5,   bfd_mach_avr5},
+  {"atmega328",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega328p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega329",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega329a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega329p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega329pa",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega3290", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega3290a",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega3290p",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega3290pa",AVR_ISA_AVR5,   bfd_mach_avr5},
+  {"atmega406",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega64rfr2", AVR_ISA_AVR5,  bfd_mach_avr5},
+  {"atmega644rfr2",AVR_ISA_AVR5,  bfd_mach_avr5},
+  {"atmega64",   AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega64a",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega640",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega644",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega644a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega644p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega644pa",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega645",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega645a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega645p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega649",  AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega649a", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega649p", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega6450", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega6450a",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega6450p",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega6490", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega6490a",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega6490p",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega64rfr2",AVR_ISA_AVR5,   bfd_mach_avr5},
+  {"atmega644rfr2",AVR_ISA_AVR5,  bfd_mach_avr5},
+  {"atmega16hva",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega16hva2",AVR_ISA_AVR5,   bfd_mach_avr5},
+  {"atmega16hvb",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega16hvbrevb",AVR_ISA_AVR5,bfd_mach_avr5},
+  {"atmega32hvb",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega32hvbrevb",AVR_ISA_AVR5,bfd_mach_avr5},
+  {"atmega64hve",AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"at90can32" , AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"at90can64" , AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"at90pwm161", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"at90pwm216", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"at90pwm316", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega32c1", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega64c1", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega16m1", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega32m1", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega64m1", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega16u4", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega32u4", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega32u6", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"at90usb646", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"at90usb647", AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"at90scr100", AVR_ISA_AVR5,    bfd_mach_avr5},
   {"at94k",      AVR_ISA_94K,     bfd_mach_avr5},
   {"at94k",      AVR_ISA_94K,     bfd_mach_avr5},
-  {"atmega2560", AVR_ISA_ALL,     bfd_mach_avr6},
-  {"atmega2561", AVR_ISA_ALL,     bfd_mach_avr6},
+  {"m3000",      AVR_ISA_AVR5,    bfd_mach_avr5},
+  {"atmega128",  AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"atmega128a", AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"atmega1280", AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"atmega1281", AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"atmega1284", AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"atmega1284p",AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"atmega128rfa1",AVR_ISA_AVR51, bfd_mach_avr51},
+  {"atmega128rfr2",AVR_ISA_AVR51, bfd_mach_avr51},
+  {"atmega1284rfr2",AVR_ISA_AVR51, bfd_mach_avr51},
+  {"at90can128", AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"at90usb1286",AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"at90usb1287",AVR_ISA_AVR51,   bfd_mach_avr51},
+  {"atmega2560", AVR_ISA_AVR6,    bfd_mach_avr6},
+  {"atmega2561", AVR_ISA_AVR6,    bfd_mach_avr6},
+  {"atmega256rfr2", AVR_ISA_AVR6, bfd_mach_avr6},
+  {"atmega2564rfr2", AVR_ISA_AVR6, bfd_mach_avr6},
+  {"atxmega16a4", AVR_ISA_XMEGA,  bfd_mach_avrxmega2},
+  {"atxmega16a4u",AVR_ISA_XMEGAU, bfd_mach_avrxmega2},
+  {"atxmega16c4", AVR_ISA_XMEGAU, bfd_mach_avrxmega2},
+  {"atxmega16d4", AVR_ISA_XMEGA,  bfd_mach_avrxmega2},
+  {"atxmega32a4", AVR_ISA_XMEGA,  bfd_mach_avrxmega2},
+  {"atxmega32a4u",AVR_ISA_XMEGAU, bfd_mach_avrxmega2},
+  {"atxmega32c4", AVR_ISA_XMEGAU, bfd_mach_avrxmega2},
+  {"atxmega32d4", AVR_ISA_XMEGA,  bfd_mach_avrxmega2},
+  {"atxmega32e5", AVR_ISA_XMEGA,  bfd_mach_avrxmega2},
+  {"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},
+  {"atxmega64b1", AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
+  {"atxmega64b3", AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
+  {"atxmega64c3", AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
+  {"atxmega64d3", AVR_ISA_XMEGA,  bfd_mach_avrxmega4},
+  {"atxmega64d4", AVR_ISA_XMEGA,  bfd_mach_avrxmega4},
+  {"atxmega64a1", AVR_ISA_XMEGA,  bfd_mach_avrxmega5},
+  {"atxmega64a1u",AVR_ISA_XMEGAU, bfd_mach_avrxmega5},
+  {"atxmega128a3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega128a3u",AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+  {"atxmega128b1", AVR_ISA_XMEGAU, bfd_mach_avrxmega6},
+  {"atxmega128b3", AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+  {"atxmega128c3", AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+  {"atxmega128d3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega128d4", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega192a3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega192a3u",AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+  {"atxmega192c3", AVR_ISA_XMEGAU, bfd_mach_avrxmega6},
+  {"atxmega192d3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega256a3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega256a3u",AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+  {"atxmega256a3b",AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega256a3bu",AVR_ISA_XMEGAU, bfd_mach_avrxmega6},
+  {"atxmega256c3", AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+  {"atxmega256d3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega384c3", AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+  {"atxmega384d3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+  {"atxmega128a1", AVR_ISA_XMEGA, bfd_mach_avrxmega7},
+  {"atxmega128a1u", AVR_ISA_XMEGAU, bfd_mach_avrxmega7},
+  {"atxmega128a4u", AVR_ISA_XMEGAU, bfd_mach_avrxmega7},
+  {"attiny4",      AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny5",      AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny9",      AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny10",     AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny20",     AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny40",     AVR_ISA_AVRTINY, bfd_mach_avrtiny},
   {NULL, 0, 0}
 };
 
   {NULL, 0, 0}
 };
 
+
 /* Current MCU type.  */
 /* Current MCU type.  */
-static struct mcu_type_s   default_mcu = {"avr2", AVR_ISA_2xxx,bfd_mach_avr2};
+static struct mcu_type_s   default_mcu = {"avr2", AVR_ISA_AVR2, bfd_mach_avr2};
+static struct mcu_type_s   specified_mcu;
 static struct mcu_type_s * avr_mcu = & default_mcu;
 
 /* AVR target-specific switches.  */
 static struct mcu_type_s * avr_mcu = & default_mcu;
 
 /* AVR target-specific switches.  */
@@ -170,9 +469,12 @@ struct avr_opt_s
   int all_opcodes;  /* -mall-opcodes: accept all known AVR opcodes.  */
   int no_skip_bug;  /* -mno-skip-bug: no warnings for skipping 2-word insns.  */
   int no_wrap;      /* -mno-wrap: reject rjmp/rcall with 8K wrap-around.  */
   int all_opcodes;  /* -mall-opcodes: accept all known AVR opcodes.  */
   int no_skip_bug;  /* -mno-skip-bug: no warnings for skipping 2-word insns.  */
   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 };
+static struct avr_opt_s avr_opt = { 0, 0, 0, 0, 0 };
 
 const char EXP_CHARS[] = "eE";
 const char FLT_CHARS[] = "dD";
 
 const char EXP_CHARS[] = "eE";
 const char FLT_CHARS[] = "dD";
@@ -195,7 +497,7 @@ const pseudo_typeS md_pseudo_table[] =
 
 struct exp_mod_s
 {
 
 struct exp_mod_s
 {
-  char *                    name;
+  const char *                    name;
   bfd_reloc_code_real_type  reloc;
   bfd_reloc_code_real_type  neg_reloc;
   int                       have_pm;
   bfd_reloc_code_real_type  reloc;
   bfd_reloc_code_real_type  neg_reloc;
   int                       have_pm;
@@ -213,7 +515,7 @@ static struct exp_mod_s exp_mod[] =
   {"hhi8",   BFD_RELOC_AVR_MS8_LDI,    BFD_RELOC_AVR_MS8_LDI_NEG,    0},
 };
 
   {"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
 {
    in a hash table which expects void * data types.  */
 typedef union
 {
@@ -227,12 +529,43 @@ static struct hash_control *avr_hash;
 /* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx).  */
 static struct hash_control *avr_mod_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
 {
   OPTION_ALL_OPCODES = OPTION_MD_BASE + 1,
   OPTION_NO_SKIP_BUG,
 #define OPTION_MMCU 'm'
 enum options
 {
   OPTION_ALL_OPCODES = OPTION_MD_BASE + 1,
   OPTION_NO_SKIP_BUG,
-  OPTION_NO_WRAP
+  OPTION_NO_WRAP,
+  OPTION_ISA_RMW,
+  OPTION_LINK_RELAX,
+  OPTION_NO_LINK_RELAX,
+  OPTION_HAVE_GCCISR
 };
 
 struct option md_longopts[] =
 };
 
 struct option md_longopts[] =
@@ -241,6 +574,10 @@ struct option md_longopts[] =
   { "mall-opcodes", no_argument, NULL, OPTION_ALL_OPCODES },
   { "mno-skip-bug", no_argument, NULL, OPTION_NO_SKIP_BUG },
   { "mno-wrap",     no_argument, NULL, OPTION_NO_WRAP     },
   { "mall-opcodes", no_argument, NULL, OPTION_ALL_OPCODES },
   { "mno-skip-bug", no_argument, NULL, OPTION_NO_SKIP_BUG },
   { "mno-wrap",     no_argument, NULL, OPTION_NO_WRAP     },
+  { "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 }
 };
 
   { NULL, no_argument, NULL, 0 }
 };
 
@@ -287,7 +624,6 @@ skip_space (char *s)
 static char *
 extract_word (char *from, char *to, int limit)
 {
 static char *
 extract_word (char *from, char *to, int limit)
 {
-  char *op_start;
   char *op_end;
   int size = 0;
 
   char *op_end;
   int size = 0;
 
@@ -296,7 +632,7 @@ extract_word (char *from, char *to, int limit)
   *to = 0;
 
   /* Find the op code end.  */
   *to = 0;
 
   /* Find the op code end.  */
-  for (op_start = op_end = from; *op_end != 0 && is_part_of_name (*op_end);)
+  for (op_end = from; *op_end != 0 && is_part_of_name (*op_end);)
     {
       to[size++] = *op_end++;
       if (size + 1 >= limit)
     {
       to[size++] = *op_end++;
       if (size + 1 >= limit)
@@ -319,21 +655,39 @@ void
 md_show_usage (FILE *stream)
 {
   fprintf (stream,
 md_show_usage (FILE *stream)
 {
   fprintf (stream,
-      _("AVR options:\n"
+      _("AVR Assembler options:\n"
        "  -mmcu=[avr-name] select microcontroller variant\n"
        "                   [avr-name] can be:\n"
        "  -mmcu=[avr-name] select microcontroller variant\n"
        "                   [avr-name] can be:\n"
-       "                   avr1 - AT90S1200, ATtiny1x, ATtiny28\n"
-       "                   avr2 - AT90S2xxx, AT90S4xxx, AT90S8xxx, ATtiny22\n"
-       "                   avr3 - ATmega103, ATmega603\n"
-       "                   avr4 - ATmega83, ATmega85\n"
-       "                   avr5 - ATmega161, ATmega163, ATmega32, AT94K\n"
-       "                   or immediate microcontroller name.\n"));
+       "                   avr1  - classic AVR core without data RAM\n"
+       "                   avr2  - classic AVR core with up to 8K program memory\n"
+       "                   avr25 - classic AVR core with up to 8K program memory\n"
+       "                           plus the MOVW instruction\n"
+       "                   avr3  - classic AVR core with up to 64K program memory\n"
+       "                   avr31 - classic AVR core with up to 128K program memory\n"
+       "                   avr35 - classic AVR core with up to 64K program memory\n"
+       "                           plus the MOVW instruction\n"
+       "                   avr4  - enhanced AVR core with up to 8K program memory\n"
+       "                   avr5  - enhanced AVR core with up to 64K program memory\n"
+       "                   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, 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"
+       "                   avrxmega7 - XMEGA, > 128K, <= 256K FLASH, > 64K RAM\n"
+       "                   avrtiny   - AVR Tiny core with 16 gp registers\n"));
   fprintf (stream,
       _("  -mall-opcodes    accept all AVR opcodes, even if not supported by MCU\n"
        "  -mno-skip-bug    disable warnings for skipping two-word instructions\n"
        "                   (default for avr4, avr5)\n"
        "  -mno-wrap        reject rjmp/rcall instructions with 8K wrap-around\n"
   fprintf (stream,
       _("  -mall-opcodes    accept all AVR opcodes, even if not supported by MCU\n"
        "  -mno-skip-bug    disable warnings for skipping two-word instructions\n"
        "                   (default for avr4, avr5)\n"
        "  -mno-wrap        reject rjmp/rcall instructions with 8K wrap-around\n"
-       "                   (default for avr3, avr5)\n"));
+       "                   (default for avr3, avr5)\n"
+       "  -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);
 }
 
   show_mcu_list (stream);
 }
 
@@ -348,26 +702,16 @@ avr_set_arch (int dummy ATTRIBUTE_UNUSED)
 }
 
 int
 }
 
 int
-md_parse_option (int c, char *arg)
+md_parse_option (int c, const char *arg)
 {
   switch (c)
     {
     case OPTION_MMCU:
       {
        int i;
 {
   switch (c)
     {
     case OPTION_MMCU:
       {
        int i;
-       char *s = alloca (strlen (arg) + 1);
-
-       {
-         char *t = s;
-         char *arg1 = arg;
-
-         do
-           *t = TOLOWER (*arg1++);
-         while (*t++);
-       }
 
        for (i = 0; mcu_types[i].name; ++i)
 
        for (i = 0; mcu_types[i].name; ++i)
-         if (strcmp (mcu_types[i].name, s) == 0)
+         if (strcasecmp (mcu_types[i].name, arg) == 0)
            break;
 
        if (!mcu_types[i].name)
            break;
 
        if (!mcu_types[i].name)
@@ -380,7 +724,12 @@ md_parse_option (int c, char *arg)
           type - this for allows passing -mmcu=... via gcc ASM_SPEC as well
           as .arch ... in the asm output at the same time.  */
        if (avr_mcu == &default_mcu || avr_mcu->mach == mcu_types[i].mach)
           type - this for allows passing -mmcu=... via gcc ASM_SPEC as well
           as .arch ... in the asm output at the same time.  */
        if (avr_mcu == &default_mcu || avr_mcu->mach == mcu_types[i].mach)
-         avr_mcu = &mcu_types[i];
+         {
+           specified_mcu.name = mcu_types[i].name;
+           specified_mcu.isa  |= mcu_types[i].isa;
+           specified_mcu.mach = mcu_types[i].mach;
+           avr_mcu = &specified_mcu;
+         }
        else
          as_fatal (_("redefinition of mcu type `%s' to `%s'"),
                    avr_mcu->name, mcu_types[i].name);
        else
          as_fatal (_("redefinition of mcu type `%s' to `%s'"),
                    avr_mcu->name, mcu_types[i].name);
@@ -395,59 +744,56 @@ md_parse_option (int c, char *arg)
     case OPTION_NO_WRAP:
       avr_opt.no_wrap = 1;
       return 1;
     case OPTION_NO_WRAP:
       avr_opt.no_wrap = 1;
       return 1;
+    case OPTION_ISA_RMW:
+      specified_mcu.isa |= AVR_ISA_RMW;
+      return 1;
+    case OPTION_LINK_RELAX:
+      avr_opt.no_link_relax = 0;
+      return 1;
+    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;
 }
 
     }
 
   return 0;
 }
 
-symbolS *
-md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
-{
-  return NULL;
-}
 
 
-/* Turn a string in input_line_pointer into a floating point constant
-   of type TYPE, and store the appropriate bytes in *LITP.  The number
-   of LITTLENUMS emitted is stored in *SIZEP.  An error message is
-   returned, or NULL on OK.  */
+/* 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.  */
 
 
-char *
-md_atof (int type, char *litP, int *sizeP)
+symbolS *
+avr_undefined_symbol (char *name)
 {
 {
-  int prec;
-  LITTLENUM_TYPE words[4];
-  LITTLENUM_TYPE *wordP;
-  char *t;
-
-  switch (type)
-    {
-    case 'f':
-      prec = 2;
-      break;
-    case 'd':
-      prec = 4;
-      break;
-    default:
-      *sizeP = 0;
-      return _("bad call to md_atof");
-    }
-
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-
-  *sizeP = prec * sizeof (LITTLENUM_TYPE);
-
-  /* This loop outputs the LITTLENUMs in REVERSE order.  */
-  for (wordP = words + prec - 1; prec--;)
+  if (ISR_CHUNK_Done != avr_isr.prev_chunk
+      && 0 == strcmp (name, "__gcc_isr.n_pushed"))
     {
     {
-      md_number_to_chars (litP, (valueT) (*wordP--), sizeof (LITTLENUM_TYPE));
-      litP += sizeof (LITTLENUM_TYPE);
+      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;
 }
 
     }
 
   return NULL;
 }
 
+const char *
+md_atof (int type, char *litP, int *sizeP)
+{
+  return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
 void
 md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
                 asection *sec ATTRIBUTE_UNUSED,
 void
 md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
                 asection *sec ATTRIBUTE_UNUSED,
@@ -480,7 +826,19 @@ md_begin (void)
       hash_insert (avr_mod_hash, EXP_MOD_NAME (i), m.ptr);
     }
 
       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);
   bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
+  linkrelax = !avr_opt.no_link_relax;
 }
 
 /* Resolve STR as a constant expression and return the result.
 }
 
 /* Resolve STR as a constant expression and return the result.
@@ -499,7 +857,7 @@ avr_get_constant (char *str, int max)
     as_bad (_("constant value required"));
 
   if (ex.X_add_number > max || ex.X_add_number < 0)
     as_bad (_("constant value required"));
 
   if (ex.X_add_number > max || ex.X_add_number < 0)
-    as_bad (_("number must be less than %d"), max + 1);
+    as_bad (_("number must be positive and less than %d"), max + 1);
 
   return ex.X_add_number;
 }
 
   return ex.X_add_number;
 }
@@ -643,7 +1001,9 @@ avr_ldi_expression (expressionS *exp)
                      break;
 
                    default:
                      break;
 
                    default:
-                     as_warn (_("expression dangerous with linker stubs"));
+                     /* PR 5523: Do not generate a warning here,
+                        legitimate code can trigger this case.  */
+                     break;
                    }
                }
              return reloc_to_return;
                    }
                }
              return reloc_to_return;
@@ -672,8 +1032,9 @@ avr_ldi_expression (expressionS *exp)
 static unsigned int
 avr_operand (struct avr_opcodes_s *opcode,
             int where,
 static unsigned int
 avr_operand (struct avr_opcodes_s *opcode,
             int where,
-            char *op,
-            char **line)
+            const char *op,
+            char **line,
+            int *pregno)
 {
   expressionS op_expr;
   unsigned int op_mask = 0;
 {
   expressionS op_expr;
   unsigned int op_mask = 0;
@@ -687,30 +1048,58 @@ avr_operand (struct avr_opcodes_s *opcode,
     case 'r':
     case 'a':
     case 'v':
     case 'r':
     case 'a':
     case 'v':
-      if (*str == 'r' || *str == 'R')
-       {
-         char r_name[20];
+      {
+        char * old_str = str;
+        char *lower;
+        char r_name[20];
 
 
-         str = extract_word (str, r_name, sizeof (r_name));
-         op_mask = 0xff;
-         if (ISDIGIT (r_name[1]))
-           {
-             if (r_name[2] == '\0')
-               op_mask = r_name[1] - '0';
-             else if (r_name[1] != '0'
-                      && ISDIGIT (r_name[2])
-                      && r_name[3] == '\0')
-               op_mask = (r_name[1] - '0') * 10 + r_name[2] - '0';
-           }
-       }
-      else
-       {
-         op_mask = avr_get_constant (str, 31);
-         str = input_line_pointer;
-       }
+        str = extract_word (str, r_name, sizeof (r_name));
+        for (lower = r_name; *lower; ++lower)
+         {
+           if (*lower >= 'A' && *lower <= 'Z')
+             *lower += 'a' - 'A';
+          }
+
+        if (r_name[0] == 'r' && ISDIGIT (r_name[1]) && r_name[2] == 0)
+          /* Single-digit register number, ie r0-r9.  */
+          op_mask = r_name[1] - '0';
+        else if (r_name[0] == 'r' && ISDIGIT (r_name[1])
+                && ISDIGIT (r_name[2]) && r_name[3] == 0)
+          /* Double-digit register number, ie r10 - r32.  */
+          op_mask = (r_name[1] - '0') * 10 + r_name[2] - '0';
+        else if (r_name[0] >= 'x' && r_name[0] <= 'z'
+                && (r_name[1] == 'l' || r_name[1] == 'h') && r_name[2] == 0)
+          /* Registers r26-r31 referred to by name, ie xl, xh, yl, yh, zl, zh.  */
+          op_mask = (r_name[0] - 'x') * 2 + (r_name[1] == 'h') + 26;
+        else if ((*op == 'v' || *op == 'w')
+                && r_name[0] >= 'x' && r_name[0] <= 'z' && r_name[1] == 0)
+          /* For the movw and addiw instructions, refer to registers x, y and z by name.  */
+          op_mask = (r_name[0] - 'x') * 2 + 26;
+        else
+          {
+            /* Numeric or symbolic constant register number.  */
+            op_mask = avr_get_constant (old_str, 31);
+            str = input_line_pointer;
+          }
+      }
+
+      if (pregno)
+       *pregno = op_mask;
+
+      if (avr_mcu->mach == bfd_mach_avrtiny)
+        {
+          if (op_mask < 16 || op_mask > 31)
+            {
+              as_bad (_("register name or number from 16 to 31 required"));
+              break;
+            }
+        }
+      else if (op_mask > 31)
+        {
+          as_bad (_("register name or number from 0 to 31 required"));
+          break;
+        }
 
 
-      if (op_mask <= 31)
-       {
          switch (*op)
            {
            case 'a':
          switch (*op)
            {
            case 'a':
@@ -738,9 +1127,6 @@ avr_operand (struct avr_opcodes_s *opcode,
              break;
            }
          break;
              break;
            }
          break;
-       }
-      as_bad (_("register name or number from 0 to 31 required"));
-      break;
 
     case 'e':
       {
 
     case 'e':
       {
@@ -788,8 +1174,19 @@ avr_operand (struct avr_opcodes_s *opcode,
       if (*str == '+')
        {
          ++str;
       if (*str == '+')
        {
          ++str;
-         op_mask |= 1;
+          const char *s;
+          for (s = opcode->opcode; *s; ++s)
+            {
+              if (*s == '+')
+                op_mask |= (1 << (15 - (s - opcode->opcode)));
+            }
        }
        }
+
+      /* attiny26 can do "lpm" and "lpm r,Z" but not "lpm r,Z+".  */
+      if (!avr_opt.all_opcodes
+         && (op_mask & 0x0001)
+         && !(avr_mcu->isa & AVR_ISA_MOVW))
+       as_bad (_("postincrement not supported"));
       break;
 
     case 'b':
       break;
 
     case 'b':
@@ -836,6 +1233,12 @@ avr_operand (struct avr_opcodes_s *opcode,
                   &op_expr, FALSE, BFD_RELOC_16);
       break;
 
                   &op_expr, FALSE, BFD_RELOC_16);
       break;
 
+    case 'j':
+      str = parse_exp (str, &op_expr);
+      fix_new_exp (frag_now, where, opcode->insn_size * 2,
+                  &op_expr, FALSE, BFD_RELOC_AVR_LDS_STS_16);
+      break;
+
     case 'M':
       {
        bfd_reloc_code_real_type r_type;
     case 'M':
       {
        bfd_reloc_code_real_type r_type;
@@ -858,6 +1261,16 @@ avr_operand (struct avr_opcodes_s *opcode,
       }
       break;
 
       }
       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);
     case 'K':
       input_line_pointer = str;
       avr_offset_expression (& op_expr);
@@ -880,22 +1293,24 @@ avr_operand (struct avr_opcodes_s *opcode,
       break;
 
     case 'P':
       break;
 
     case 'P':
-      {
-       unsigned int x;
-
-       x = avr_get_constant (str, 63);
-       str = input_line_pointer;
-       op_mask |= (x & 0xf) | ((x & 0x30) << 5);
-      }
+      str = parse_exp (str, &op_expr);
+      fix_new_exp (frag_now, where, opcode->insn_size * 2,
+                    &op_expr, FALSE, BFD_RELOC_AVR_PORT6);
       break;
 
     case 'p':
       break;
 
     case 'p':
+      str = parse_exp (str, &op_expr);
+      fix_new_exp (frag_now, where, opcode->insn_size * 2,
+                    &op_expr, FALSE, BFD_RELOC_AVR_PORT5);
+      break;
+
+    case 'E':
       {
        unsigned int x;
 
       {
        unsigned int x;
 
-       x = avr_get_constant (str, 31);
+       x = avr_get_constant (str, 15);
        str = input_line_pointer;
        str = input_line_pointer;
-       op_mask |= x << 3;
+       op_mask |= (x << 4);
       }
       break;
 
       }
       break;
 
@@ -910,18 +1325,28 @@ avr_operand (struct avr_opcodes_s *opcode,
   return op_mask;
 }
 
   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.  */
 
 static unsigned int
 avr_operands (struct avr_opcodes_s *opcode, char **line)
 {
 /* Parse instruction operands.
    Return binary opcode.  */
 
 static unsigned int
 avr_operands (struct avr_opcodes_s *opcode, char **line)
 {
-  char *op = opcode->constraints;
+  const char *op = opcode->constraints;
   unsigned int bin = opcode->bin_opcode;
   char *frag = frag_more (opcode->insn_size * 2);
   char *str = *line;
   int where = frag - frag_now->fr_literal;
   unsigned int bin = opcode->bin_opcode;
   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)
 
   /* Opcode have operands.  */
   if (*op)
@@ -934,7 +1359,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
       /* Parse first operand.  */
       if (REGISTER_P (*op))
        reg1_present = 1;
       /* 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.  */
       ++op;
 
       /* Parse second operand.  */
@@ -947,6 +1372,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
            {
              reg2 = reg1;
              reg2_present = 1;
            {
              reg2 = reg1;
              reg2_present = 1;
+             regno2 = regno1;
            }
          else
            {
            }
          else
            {
@@ -958,7 +1384,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
                as_bad (_("`,' required"));
              str = skip_space (str);
 
                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)
            }
 
          if (reg1_present && reg2_present)
@@ -971,6 +1397,9 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
       bin |= reg1 | reg2;
     }
 
       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"));
   /* Detect undefined combinations (like ld r31,Z+).  */
   if (!avr_opt.all_opcodes && AVR_UNDEF_P (bin))
     as_warn (_("undefined combination of operands"));
@@ -981,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 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);
        as_warn (_("skipping two-word instruction"));
 
       bfd_putl32 ((bfd_vma) bin, frag);
@@ -989,7 +1418,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
   else
     bfd_putl16 ((bfd_vma) bin, frag);
 
   else
     bfd_putl16 ((bfd_vma) bin, frag);
 
-  prev = bin;
+  frag_now->tc_frag_data.prev_opcode = bin;
   *line = str;
   return bin;
 }
   *line = str;
   return bin;
 }
@@ -1000,8 +1429,8 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
 valueT
 md_section_align (asection *seg, valueT addr)
 {
 valueT
 md_section_align (asection *seg, valueT addr)
 {
-  int align = bfd_get_section_alignment (stdoutput, seg);
-  return ((addr + (1 << align) - 1) & (-1 << align));
+  int align = bfd_section_alignment (seg);
+  return ((addr + (1 << align) - 1) & (-1UL << align));
 }
 
 /* If you define this macro, it should return the offset between the
 }
 
 /* If you define this macro, it should return the offset between the
@@ -1021,6 +1450,55 @@ md_pcrel_from_section (fixS *fixp, segT sec)
   return fixp->fx_frag->fr_address + fixp->fx_where;
 }
 
   return fixp->fx_frag->fr_address + fixp->fx_where;
 }
 
+static bfd_boolean
+relaxable_section (asection *sec)
+{
+  return ((sec->flags & SEC_DEBUGGING) == 0
+          && (sec->flags & SEC_CODE) != 0
+          && (sec->flags & SEC_ALLOC) != 0);
+}
+
+/* Does whatever the xtensa port does.  */
+int
+avr_validate_fix_sub (fixS *fix)
+{
+  segT add_symbol_segment, sub_symbol_segment;
+
+  /* The difference of two symbols should be resolved by the assembler when
+     linkrelax is not set.  If the linker may relax the section containing
+     the symbols, then an Xtensa DIFF relocation must be generated so that
+     the linker knows to adjust the difference value.  */
+  if (!linkrelax || fix->fx_addsy == NULL)
+    return 0;
+
+  /* Make sure both symbols are in the same segment, and that segment is
+     "normal" and relaxable.  If the segment is not "normal", then the
+     fix is not valid.  If the segment is not "relaxable", then the fix
+     should have been handled earlier.  */
+  add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+  if (! SEG_NORMAL (add_symbol_segment) ||
+      ! relaxable_section (add_symbol_segment))
+    return 0;
+
+  sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+  return (sub_symbol_segment == add_symbol_segment);
+}
+
+/* TC_FORCE_RELOCATION hook */
+
+/* If linkrelax is turned on, and the symbol to relocate
+   against is in a relaxable segment, don't compute the value -
+   generate a relocation instead.  */
+int
+avr_force_relocation (fixS *fix)
+{
+  if (linkrelax && fix->fx_addsy
+      && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+    return 1;
+
+  return generic_force_reloc (fix);
+}
+
 /* GAS will call this for each fixup.  It should store the correct
    value in the object file.  */
 
 /* GAS will call this for each fixup.  It should store the correct
    value in the object file.  */
 
@@ -1044,11 +1522,48 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
          fixP->fx_done = 1;
        }
     }
          fixP->fx_done = 1;
        }
     }
-
+  else if (linkrelax && fixP->fx_subsy)
+    {
+      /* For a subtraction relocation expression, generate one
+         of the DIFF relocs, with the value being the difference.
+         Note that a sym1 - sym2 expression is adjusted into a
+         section_start_sym + sym4_offset_from_section_start - sym1
+         expression. fixP->fx_addsy holds the section start symbol,
+         fixP->fx_offset holds sym2's offset, and fixP->fx_subsy
+         holds sym1. Calculate the current difference and write value,
+         but leave fx_offset as is - during relaxation,
+         fx_offset - value gives sym1's value.  */
+
+       switch (fixP->fx_r_type)
+         {
+           case BFD_RELOC_8:
+             fixP->fx_r_type = BFD_RELOC_AVR_DIFF8;
+             break;
+           case BFD_RELOC_16:
+             fixP->fx_r_type = BFD_RELOC_AVR_DIFF16;
+             break;
+           case BFD_RELOC_32:
+             fixP->fx_r_type = BFD_RELOC_AVR_DIFF32;
+             break;
+           default:
+             as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+             break;
+         }
+
+      value = S_GET_VALUE (fixP->fx_addsy) +
+          fixP->fx_offset - S_GET_VALUE (fixP->fx_subsy);
+      *valP = value;
+
+      fixP->fx_subsy = NULL;
+  }
   /* We don't actually support subtracting a symbol.  */
   if (fixP->fx_subsy != (symbolS *) NULL)
     as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
 
   /* We don't actually support subtracting a symbol.  */
   if (fixP->fx_subsy != (symbolS *) NULL)
     as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
 
+  /* For the DIFF relocs, write the value into the object file while still
+     keeping fx_done FALSE, as both the difference (recorded in the object file)
+     and the sym offset (part of fixP) are needed at link relax time.  */
+  where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
   switch (fixP->fx_r_type)
     {
     default:
   switch (fixP->fx_r_type)
     {
     default:
@@ -1058,6 +1573,16 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
     case BFD_RELOC_AVR_13_PCREL:
     case BFD_RELOC_32:
     case BFD_RELOC_16:
     case BFD_RELOC_AVR_13_PCREL:
     case BFD_RELOC_32:
     case BFD_RELOC_16:
+      break;
+    case BFD_RELOC_AVR_DIFF8:
+      *where = value;
+         break;
+    case BFD_RELOC_AVR_DIFF16:
+      bfd_putl16 ((bfd_vma) value, where);
+      break;
+    case BFD_RELOC_AVR_DIFF32:
+      bfd_putl32 ((bfd_vma) value, where);
+      break;
     case BFD_RELOC_AVR_CALL:
       break;
     }
     case BFD_RELOC_AVR_CALL:
       break;
     }
@@ -1109,13 +1634,20 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
          break;
 
        case BFD_RELOC_32:
          break;
 
        case BFD_RELOC_32:
-         bfd_putl16 ((bfd_vma) value, where);
+         bfd_putl32 ((bfd_vma) value, where);
          break;
 
        case BFD_RELOC_16:
          bfd_putl16 ((bfd_vma) value, where);
          break;
 
          break;
 
        case BFD_RELOC_16:
          bfd_putl16 ((bfd_vma) value, where);
          break;
 
+       case BFD_RELOC_8:
+          if (value > 255 || value < -128)
+           as_warn_where (fixP->fx_file, fixP->fx_line,
+                           _("operand out of range: %ld"), value);
+          *where = value;
+         break;
+
        case BFD_RELOC_AVR_16_PM:
          bfd_putl16 ((bfd_vma) (value >> 1), where);
          break;
        case BFD_RELOC_AVR_16_PM:
          bfd_putl16 ((bfd_vma) (value >> 1), where);
          break;
@@ -1127,11 +1659,21 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where);
          break;
 
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where);
          break;
 
+       case BFD_RELOC_AVR_LDS_STS_16:
+         if ((value < 0x40) || (value > 0xBF))
+           as_warn_where (fixP->fx_file, fixP->fx_line,
+                          _("operand out of range: 0x%lx"),
+                          (unsigned long)value);
+         insn |= ((value & 0xF) | ((value & 0x30) << 5) | ((value & 0x40) << 2));
+         bfd_putl16 ((bfd_vma) insn, where);
+         break;
+
        case BFD_RELOC_AVR_6:
          if ((value > 63) || (value < 0))
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("operand out of range: %ld"), value);
        case BFD_RELOC_AVR_6:
          if ((value > 63) || (value < 0))
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("operand out of range: %ld"), value);
-         bfd_putl16 ((bfd_vma) insn | ((value & 7) | ((value & (3 << 3)) << 7) | ((value & (1 << 5)) << 8)), where);
+         bfd_putl16 ((bfd_vma) insn | ((value & 7) | ((value & (3 << 3)) << 7)
+                                       | ((value & (1 << 5)) << 8)), where);
          break;
 
        case BFD_RELOC_AVR_6_ADIW:
          break;
 
        case BFD_RELOC_AVR_6_ADIW:
@@ -1212,15 +1754,41 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
          }
          break;
 
          }
          break;
 
-       default:
+        case BFD_RELOC_AVR_8_LO:
+          *where = 0xff & value;
+          break;
+
+        case BFD_RELOC_AVR_8_HI:
+          *where = 0xff & (value >> 8);
+          break;
+
+        case BFD_RELOC_AVR_8_HLO:
+          *where = 0xff & (value >> 16);
+          break;
+
+        default:
          as_fatal (_("line %d: unknown relocation type: 0x%x"),
                    fixP->fx_line, fixP->fx_r_type);
          break;
          as_fatal (_("line %d: unknown relocation type: 0x%x"),
                    fixP->fx_line, fixP->fx_r_type);
          break;
+
+       case BFD_RELOC_AVR_PORT6:
+         if (value > 63)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("operand out of range: %ld"), value);
+         bfd_putl16 ((bfd_vma) insn | ((value & 0x30) << 5) | (value & 0x0f), where);
+         break;
+
+       case BFD_RELOC_AVR_PORT5:
+         if (value > 31)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("operand out of range: %ld"), value);
+         bfd_putl16 ((bfd_vma) insn | ((value & 0x1f) << 3), where);
+         break;
        }
     }
   else
     {
        }
     }
   else
     {
-      switch (fixP->fx_r_type)
+      switch ((int) fixP->fx_r_type)
        {
        case -BFD_RELOC_AVR_HI8_LDI_NEG:
        case -BFD_RELOC_AVR_HI8_LDI:
        {
        case -BFD_RELOC_AVR_HI8_LDI_NEG:
        case -BFD_RELOC_AVR_HI8_LDI:
@@ -1250,40 +1818,35 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED,
              fixS *fixp)
 {
   arelent *reloc;
              fixS *fixp)
 {
   arelent *reloc;
+  bfd_reloc_code_real_type code = fixp->fx_r_type;
 
 
-  if (fixp->fx_addsy && fixp->fx_subsy)
+  if (fixp->fx_subsy != NULL)
     {
     {
-      long value = 0;
+      as_bad_where (fixp->fx_file, fixp->fx_line, _("expression too complex"));
+      return NULL;
+    }
 
 
-      if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
-          || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section)
-        {
-          as_bad_where (fixp->fx_file, fixp->fx_line,
-              "Difference of symbols in different sections is not supported");
-          return NULL;
-        }
+  reloc = XNEW (arelent);
 
 
-      /* We are dealing with two symbols defined in the same section.
-         Let us fix-up them here.  */
-      value += S_GET_VALUE (fixp->fx_addsy);
-      value -= S_GET_VALUE (fixp->fx_subsy);
+  reloc->sym_ptr_ptr = XNEW (asymbol *);
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
 
 
-      /* When fx_addsy and fx_subsy both are zero, md_apply_fix
-         only takes it's second operands for the fixup value.  */
-      fixp->fx_addsy = NULL;
-      fixp->fx_subsy = NULL;
-      md_apply_fix (fixp, (valueT *) &value, NULL);
+  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
 
-      return NULL;
+  if ((fixp->fx_r_type == BFD_RELOC_32) && (fixp->fx_pcrel))
+    {
+      if (seg->use_rela_p)
+        fixp->fx_offset -= md_pcrel_from_section (fixp, seg);
+      else
+        fixp->fx_offset = reloc->address;
+
+      code = BFD_RELOC_32_PCREL;
     }
 
     }
 
-  reloc = xmalloc (sizeof (arelent));
+  reloc->addend = fixp->fx_offset;
 
 
-  reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
-  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
 
 
-  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
-  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
   if (reloc->howto == (reloc_howto_type *) NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
   if (reloc->howto == (reloc_howto_type *) NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
@@ -1296,7 +1859,6 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED,
       || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     reloc->address = fixp->fx_offset;
 
       || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     reloc->address = fixp->fx_offset;
 
-  reloc->addend = fixp->fx_offset;
 
   return reloc;
 }
 
   return reloc;
 }
@@ -1314,108 +1876,935 @@ md_assemble (char *str)
 
   opcode = (struct avr_opcodes_s *) hash_find (avr_hash, op);
 
 
   opcode = (struct avr_opcodes_s *) hash_find (avr_hash, op);
 
+  if (opcode && !avr_opt.all_opcodes)
+    {
+      /* Check if the instruction's ISA bit is ON in the ISA bits of the part
+         specified by the user.  If not look for other instructions
+        specifications with same mnemonic who's ISA bits matches.
+
+         This requires include/opcode/avr.h to have the instructions with
+         same mnemonic to be specified in sequence.  */
+
+      while ((opcode->isa & avr_mcu->isa) != opcode->isa)
+        {
+          opcode++;
+
+          if (opcode->name && strcmp(op, opcode->name))
+            {
+              as_bad (_("illegal opcode %s for mcu %s"),
+                      opcode->name, avr_mcu->name);
+              return;
+            }
+        }
+    }
+
   if (opcode == NULL)
     {
       as_bad (_("unknown opcode `%s'"), op);
       return;
     }
 
   if (opcode == NULL)
     {
       as_bad (_("unknown opcode `%s'"), op);
       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.  */
 
   if (*str && *opcode->constraints == '?')
     ++opcode;
 
   /* Special case for opcodes with optional operands (lpm, elpm) -
      version with operands exists in avr_opcodes[] in the next entry.  */
 
   if (*str && *opcode->constraints == '?')
     ++opcode;
 
-  if (!avr_opt.all_opcodes && (opcode->isa & avr_mcu->isa) != opcode->isa)
-    as_bad (_("illegal opcode %s for mcu %s"), opcode->name, avr_mcu->name);
+  dwarf2_emit_insn (0);
 
   /* We used to set input_line_pointer to the result of get_operands,
      but that is wrong.  Our caller assumes we don't change it.  */
   {
     char *t = input_line_pointer;
 
 
   /* We used to set input_line_pointer to the result of get_operands,
      but that is wrong.  Our caller assumes we don't change it.  */
   {
     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;
   }
 }
 
     if (*skip_space (str))
       as_bad (_("garbage at end of line"));
     input_line_pointer = t;
   }
 }
 
-/* Flag to pass `pm' mode between `avr_parse_cons_expression' and
-   `avr_cons_fix_new'.  */
-static int exp_mod_pm = 0;
+const exp_mod_data_t exp_mod_data[] =
+{
+  /* Default, must be first.  */
+  { "", 0, BFD_RELOC_16, "" },
+  /* Divides by 2 to get word address.  Generate Stub.  */
+  { "gs", 2, BFD_RELOC_AVR_16_PM, "`gs' " },
+  { "pm", 2, BFD_RELOC_AVR_16_PM, "`pm' " },
+  /* The following are used together with avr-gcc's __memx address space
+     in order to initialize a 24-bit pointer variable with a 24-bit address.
+     For address in flash, hlo8 will contain the flash segment if the
+     symbol is located in flash. If the symbol is located in RAM; hlo8
+     will contain 0x80 which matches avr-gcc's notion of how 24-bit RAM/flash
+     addresses linearize address space.  */
+  { "lo8",  1, BFD_RELOC_AVR_8_LO,  "`lo8' "  },
+  { "hi8",  1, BFD_RELOC_AVR_8_HI,  "`hi8' "  },
+  { "hlo8", 1, BFD_RELOC_AVR_8_HLO, "`hlo8' " },
+  { "hh8",  1, BFD_RELOC_AVR_8_HLO, "`hh8' "  },
+};
 
 
-/* Parse special CONS expression: pm (expression)
-   or alternatively: gs (expression).
-   These are used for addressing program memory.
-   Relocation: BFD_RELOC_AVR_16_PM.  */
+/* Parse special CONS expression: pm (expression) or alternatively
+   gs (expression).  These are used for addressing program memory.  Moreover,
+   define lo8 (expression), hi8 (expression) and hlo8 (expression).  */
 
 
-void
+const exp_mod_data_t *
 avr_parse_cons_expression (expressionS *exp, int nbytes)
 {
   char *tmp;
 avr_parse_cons_expression (expressionS *exp, int nbytes)
 {
   char *tmp;
-
-  exp_mod_pm = 0;
+  unsigned int i;
 
   tmp = input_line_pointer = skip_space (input_line_pointer);
 
 
   tmp = input_line_pointer = skip_space (input_line_pointer);
 
-  if (nbytes == 2)
+  /* The first entry of exp_mod_data[] contains an entry if no
+     expression modifier is present.  Skip it.  */
+
+  for (i = 0; i < ARRAY_SIZE (exp_mod_data); i++)
     {
     {
-      char *pm_name1 = "pm";
-      char *pm_name2 = "gs";
-      int len = strlen (pm_name1);
-      /* len must be the same for both pm identifiers.  */
+      const exp_mod_data_t *pexp = &exp_mod_data[i];
+      int len = strlen (pexp->name);
 
 
-      if (strncasecmp (input_line_pointer, pm_name1, len) == 0
-          || strncasecmp (input_line_pointer, pm_name2, len) == 0)
+      if (nbytes == pexp->nbytes
+          && strncasecmp (input_line_pointer, pexp->name, len) == 0)
        {
          input_line_pointer = skip_space (input_line_pointer + len);
 
          if (*input_line_pointer == '(')
            {
              input_line_pointer = skip_space (input_line_pointer + 1);
        {
          input_line_pointer = skip_space (input_line_pointer + len);
 
          if (*input_line_pointer == '(')
            {
              input_line_pointer = skip_space (input_line_pointer + 1);
-             exp_mod_pm = 1;
              expression (exp);
 
              if (*input_line_pointer == ')')
              expression (exp);
 
              if (*input_line_pointer == ')')
-               ++input_line_pointer;
+               {
+                 ++input_line_pointer;
+                 return pexp;
+               }
              else
                {
                  as_bad (_("`)' required"));
              else
                {
                  as_bad (_("`)' required"));
-                 exp_mod_pm = 0;
+                 return &exp_mod_data[0];
                }
                }
-
-             return;
            }
 
          input_line_pointer = tmp;
            }
 
          input_line_pointer = tmp;
+
+          break;
        }
     }
 
   expression (exp);
        }
     }
 
   expression (exp);
+  return &exp_mod_data[0];
 }
 
 void
 avr_cons_fix_new (fragS *frag,
                  int where,
                  int nbytes,
 }
 
 void
 avr_cons_fix_new (fragS *frag,
                  int where,
                  int nbytes,
-                 expressionS *exp)
+                 expressionS *exp,
+                 const exp_mod_data_t *pexp_mod_data)
 {
 {
-  if (exp_mod_pm == 0)
+  int bad = 0;
+
+  switch (pexp_mod_data->reloc)
     {
     {
-      if (nbytes == 2)
+    default:
+      if (nbytes == 1)
+       fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_8);
+      else if (nbytes == 2)
        fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_16);
       else if (nbytes == 4)
        fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_32);
       else
        fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_16);
       else if (nbytes == 4)
        fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_32);
       else
-       as_bad (_("illegal %srelocation size: %d"), "", nbytes);
+       bad = 1;
+      break;
+
+    case BFD_RELOC_AVR_16_PM:
+    case BFD_RELOC_AVR_8_LO:
+    case BFD_RELOC_AVR_8_HI:
+    case BFD_RELOC_AVR_8_HLO:
+      if (nbytes == pexp_mod_data->nbytes)
+        fix_new_exp (frag, where, nbytes, exp, FALSE, pexp_mod_data->reloc);
+      else
+        bad = 1;
+      break;
+    }
+
+  if (bad)
+    as_bad (_("illegal %s relocation size: %d"), pexp_mod_data->error, nbytes);
+}
+
+static bfd_boolean
+mcu_has_3_byte_pc (void)
+{
+  int mach = avr_mcu->mach;
+
+  return mach == bfd_mach_avr6
+    || mach == bfd_mach_avrxmega6
+    || mach == bfd_mach_avrxmega7;
+}
+
+void
+tc_cfi_frame_initial_instructions (void)
+{
+  /* AVR6 pushes 3 bytes for calls.  */
+  int return_size = (mcu_has_3_byte_pc () ? 3 : 2);
+
+  /* The CFA is the caller's stack location before the call insn.  */
+  /* Note that the stack pointer is dwarf register number 32.  */
+  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 targets that use pre-decrement.  */
+  cfi_add_CFA_offset (DWARF2_DEFAULT_RETURN_COLUMN, 1-return_size);
+}
+
+bfd_boolean
+avr_allow_local_subtract (expressionS * left,
+                            expressionS * right,
+                            segT section)
+{
+  /* If we are not in relaxation mode, subtraction is OK.  */
+  if (!linkrelax)
+    return TRUE;
+
+  /* If the symbols are not in a code section then they are OK.  */
+  if ((section->flags & SEC_CODE) == 0)
+    return TRUE;
+
+  if (left->X_add_symbol == right->X_add_symbol)
+    return TRUE;
+
+  /* We have to assume that there may be instructions between the
+     two symbols and that relaxation may increase the distance between
+     them.  */
+  return FALSE;
+}
+
+void
+avr_elf_final_processing (void)
+{
+  if (linkrelax)
+    elf_elfheader (stdoutput)->e_flags |= EF_AVR_LINKRELAX_PREPARED;
+}
+
+/* Write out the header of a .avr.prop section into the area pointed to by
+   DATA.  The RECORD_COUNT will be placed in the header as the number of
+   records that are to follow.
+   The area DATA must be big enough the receive the header, which is
+   AVR_PROPERTY_SECTION_HEADER_SIZE bytes long.  */
+
+static char *
+avr_output_property_section_header (char *data,
+                                    unsigned int record_count)
+{
+  char *orig_data = data;
+
+  md_number_to_chars (data, AVR_PROPERTY_RECORDS_VERSION, 1);
+  data++;
+  /* There's space for a single byte flags field, but right now there's
+     nothing to go in here, so just set the value to zero.  */
+  md_number_to_chars (data, 0, 1);
+  data++;
+  md_number_to_chars (data, record_count, 2);
+  data+=2;
+
+  gas_assert (data - orig_data == AVR_PROPERTY_SECTION_HEADER_SIZE);
+
+  return data;
+}
+
+/* Return the number of bytes required to store RECORD into the .avr.prop
+   section. The size returned is the compressed size that corresponds to
+   how the record will be written out in AVR_OUTPUT_PROPERTY_RECORD.  */
+
+static int
+avr_record_size (const struct avr_property_record *record)
+{
+  /* The first 5 bytes are a 4-byte address, followed by a 1-byte type
+     identifier.  */
+  int size = 5;
+
+  switch (record->type)
+    {
+    case RECORD_ORG:
+      size += 0; /* No extra information.  */
+      break;
+
+    case RECORD_ORG_AND_FILL:
+      size += 4; /* A 4-byte fill value.  */
+      break;
+
+    case RECORD_ALIGN:
+      size += 4; /* A 4-byte alignment value.  */
+      break;
+
+    case RECORD_ALIGN_AND_FILL:
+      size += 8; /* A 4-byte alignment, and 4-byte fill value.  */
+      break;
+
+    default:
+      as_fatal (_("unknown record type %d (in %s)"),
+                record->type, __PRETTY_FUNCTION__);
+    }
+
+  return size;
+}
+
+/* Write out RECORD.  FRAG_BASE points to the start of the data area setup
+   to hold all of the .avr.prop content, FRAG_PTR points to the next
+   writable location.  The data area must be big enough to hold all of the
+   records.  The size of the data written out for this RECORD must match
+   the size from AVR_RECORD_SIZE.  */
+
+static char *
+avr_output_property_record (char * const frag_base, char *frag_ptr,
+                            const struct avr_property_record *record)
+{
+  fixS *fix;
+  int where;
+  char *init_frag_ptr = frag_ptr;
+
+  where = frag_ptr - frag_base;
+  fix = fix_new (frag_now, where, 4,
+                 section_symbol (record->section),
+                 record->offset, FALSE, BFD_RELOC_32);
+  fix->fx_file = "<internal>";
+  fix->fx_line = 0;
+  frag_ptr += 4;
+
+  md_number_to_chars (frag_ptr, (bfd_byte) record->type, 1);
+  frag_ptr += 1;
+
+  /* Write out the rest of the data.  */
+  switch (record->type)
+    {
+    case RECORD_ORG:
+      break;
+
+    case RECORD_ORG_AND_FILL:
+      md_number_to_chars (frag_ptr, record->data.org.fill, 4);
+      frag_ptr += 4;
+      break;
+
+    case RECORD_ALIGN:
+      md_number_to_chars (frag_ptr, record->data.align.bytes, 4);
+      frag_ptr += 4;
+      break;
+
+    case RECORD_ALIGN_AND_FILL:
+      md_number_to_chars (frag_ptr, record->data.align.bytes, 4);
+      md_number_to_chars (frag_ptr + 4, record->data.align.fill, 4);
+      frag_ptr += 8;
+      break;
+
+    default:
+      as_fatal (_("unknown record type %d (in %s)"),
+                record->type, __PRETTY_FUNCTION__);
+    }
+
+  gas_assert (frag_ptr - init_frag_ptr == avr_record_size (record));
+
+  return frag_ptr;
+}
+
+/* Create the section to hold the AVR property information.  Return the
+   section.  */
+
+static asection *
+avr_create_property_section (void)
+{
+  asection *sec;
+  flagword flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY);
+  const char *section_name = AVR_PROPERTY_RECORD_SECTION_NAME;
+
+  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 (sec, flags);
+  sec->output_section = sec;
+  return sec;
+}
+
+/* This hook is called when alignment is performed, and allows us to
+   capture the details of both .org and .align directives.  */
+
+void
+avr_handle_align (fragS *fragP)
+{
+  if (linkrelax)
+    {
+      /* Ignore alignment requests at FR_ADDRESS 0, these are at the very
+         start of a section, and will be handled by the standard section
+         alignment mechanism.  */
+      if ((fragP->fr_type == rs_align
+           || fragP->fr_type == rs_align_code)
+          && fragP->fr_offset > 0)
+        {
+          char *p = fragP->fr_literal + fragP->fr_fix;
+
+          fragP->tc_frag_data.is_align = TRUE;
+          fragP->tc_frag_data.alignment = fragP->fr_offset;
+          fragP->tc_frag_data.fill = *p;
+          fragP->tc_frag_data.has_fill = (fragP->tc_frag_data.fill != 0);
+        }
+
+      if (fragP->fr_type == rs_org && fragP->fr_offset > 0)
+        {
+          char *p = fragP->fr_literal + fragP->fr_fix;
+
+          fragP->tc_frag_data.is_org = TRUE;
+          fragP->tc_frag_data.fill = *p;
+          fragP->tc_frag_data.has_fill = (fragP->tc_frag_data.fill != 0);
+        }
+    }
+}
+
+/* Return TRUE if this section is not one for which we need to record
+   information in the avr property section.  */
+
+static bfd_boolean
+exclude_section_from_property_tables (segT sec)
+{
+  /* Only generate property information for sections on which linker
+     relaxation could be performed.  */
+  return !relaxable_section (sec);
+}
+
+/* Create a property record for fragment FRAGP from section SEC and place
+   it into an AVR_PROPERTY_RECORD_LINK structure, which can then formed
+   into a linked list by the caller.  */
+
+static struct avr_property_record_link *
+create_record_for_frag (segT sec, fragS *fragP)
+{
+  struct avr_property_record_link *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)
+    {
+      prop_rec_link->record.offset = fragP->fr_next->fr_address;
+      prop_rec_link->record.section = sec;
+
+      if (fragP->tc_frag_data.has_fill)
+        {
+          prop_rec_link->record.data.org.fill = fragP->tc_frag_data.fill;
+          prop_rec_link->record.type = RECORD_ORG_AND_FILL;
+        }
+      else
+        prop_rec_link->record.type = RECORD_ORG;
+    }
+  else
+    {
+      prop_rec_link->record.offset = fragP->fr_next->fr_address;
+      prop_rec_link->record.section = sec;
+
+      gas_assert (fragP->tc_frag_data.is_align);
+      if (fragP->tc_frag_data.has_fill)
+        {
+          prop_rec_link->record.data.align.fill = fragP->tc_frag_data.fill;
+          prop_rec_link->record.type = RECORD_ALIGN_AND_FILL;
+        }
+      else
+        prop_rec_link->record.type = RECORD_ALIGN;
+      prop_rec_link->record.data.align.bytes = fragP->tc_frag_data.alignment;
+    }
+
+  return prop_rec_link;
+}
+
+/* Build a list of AVR_PROPERTY_RECORD_LINK structures for section SEC, and
+   merged them onto the list pointed to by NEXT_PTR.  Return a pointer to
+   the last list item created.  */
+
+static struct avr_property_record_link **
+append_records_for_section (segT sec,
+                            struct avr_property_record_link **next_ptr)
+{
+  segment_info_type *seginfo = seg_info (sec);
+  fragS *fragP;
+
+  if (seginfo && seginfo->frchainP)
+    {
+      for (fragP = seginfo->frchainP->frch_root;
+           fragP;
+           fragP = fragP->fr_next)
+       {
+          if (fragP->tc_frag_data.is_align
+              || fragP->tc_frag_data.is_org)
+            {
+              /* Create a single new entry.  */
+              struct avr_property_record_link *new_link
+                = create_record_for_frag (sec, fragP);
+
+              *next_ptr = new_link;
+              next_ptr = &new_link->next;
+            }
+       }
+    }
+
+  return next_ptr;
+}
+
+/* Create the AVR property section and fill it with records of .org and
+   .align directives that were used.  The section is only created if it
+   will actually have any content.  */
+
+static void
+avr_create_and_fill_property_section (void)
+{
+  segT *seclist;
+  asection *prop_sec;
+  struct avr_property_record_link *r_list, **next_ptr;
+  char *frag_ptr, *frag_base;
+  bfd_size_type sec_size;
+  struct avr_property_record_link *rec;
+  unsigned int record_count;
+
+  /* First walk over all sections.  For sections on which linker
+     relaxation could be applied, extend the record list.  The record list
+     holds information that the linker will need to know.  */
+
+  prop_sec = NULL;
+  r_list = NULL;
+  next_ptr = &r_list;
+  for (seclist = &stdoutput->sections;
+       seclist && *seclist;
+       seclist = &(*seclist)->next)
+    {
+      segT sec = *seclist;
+
+      if (exclude_section_from_property_tables (sec))
+       continue;
+
+      next_ptr = append_records_for_section (sec, next_ptr);
+    }
+
+  /* Create property section and ensure the size is correct.  We've already
+     passed the point where gas could size this for us.  */
+  sec_size = AVR_PROPERTY_SECTION_HEADER_SIZE;
+  record_count = 0;
+  for (rec = r_list; rec != NULL; rec = rec->next)
+    {
+      record_count++;
+      sec_size += avr_record_size (&rec->record);
+    }
+
+  if (record_count == 0)
+    return;
+
+  prop_sec = avr_create_property_section ();
+  bfd_set_section_size (prop_sec, sec_size);
+
+  subseg_set (prop_sec, 0);
+  frag_base = frag_more (sec_size);
+
+  frag_ptr =
+    avr_output_property_section_header (frag_base, record_count);
+
+  for (rec = r_list; rec != NULL; rec = rec->next)
+    frag_ptr = avr_output_property_record (frag_base, frag_ptr, &rec->record);
+
+  frag_wane (frag_now);
+  frag_new (0);
+  frag_wane (frag_now);
+}
+
+/* We're using this hook to build up the AVR property section.  It's called
+   late in the assembly process which suits our needs.  */
+void
+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
     }
   else
+    abort();
+
+  if (strchr (op->constraints, 'P'))
     {
     {
-      if (nbytes == 2)
-       fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_AVR_16_PM);
+      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
       else
-       as_bad (_("illegal %srelocation size: %d"), "`pm' ", nbytes);
-      exp_mod_pm = 0;
+       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.04955 seconds and 4 git commands to generate.