/* tc-avr.c -- Assembler code for the ATMEL AVR
- Copyright 1999, 2000, 2001, 2002, 2004, 2005
- Free Software Foundation, Inc.
+ Copyright (C) 1999-2019 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
- 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,
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to
- the Free Software Foundation, 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
-#include <stdio.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
{
- char *name;
- char *constraints;
- int insn_size; /* In words. */
- int isa;
- unsigned int bin_opcode;
+ 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) \
-{#NAME, CONSTR, SIZE, ISA, BIN},
+{#NAME, CONSTR, OPCODE, SIZE, ISA, BIN},
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 *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
- 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[] =
{
- {"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},
- {"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},
- {"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_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_M8, bfd_mach_avr4},
- {"atmega8515",AVR_ISA_M8, bfd_mach_avr4},
- {"atmega8535",AVR_ISA_M8, bfd_mach_avr4},
- {"atmega16", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega161", AVR_ISA_M161, bfd_mach_avr5},
- {"atmega162", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega163", AVR_ISA_M161, bfd_mach_avr5},
- {"atmega165", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega168", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega169", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega32", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega323", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega325", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega3250",AVR_ISA_M323, bfd_mach_avr5},
- {"atmega64", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega128", AVR_ISA_M128, bfd_mach_avr5},
- {"atmega645", AVR_ISA_M323, bfd_mach_avr5},
- {"atmega6450",AVR_ISA_M323, bfd_mach_avr5},
- {"at90can128",AVR_ISA_M128, bfd_mach_avr5},
- {"at94k", AVR_ISA_94K, bfd_mach_avr5},
+ {"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},
+ {"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},
+ {"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},
+ {"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},
+ {"atmega162", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega163", AVR_ISA_M161, 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},
+ {"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}
};
+
/* Current MCU type. */
-static struct mcu_type_s default_mcu = {"avr2", AVR_ISA_2xxx,bfd_mach_avr2};
-static struct mcu_type_s *avr_mcu = &default_mcu;
+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. */
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";
-static void avr_set_arch (int dummy);
+
+static void avr_set_arch (int);
/* The target specific pseudo-ops which we support. */
const pseudo_typeS md_pseudo_table[] =
#define LDI_IMMEDIATE(x) (((x) & 0xf) | (((x) << 4) & 0xf00))
-static void show_mcu_list PARAMS ((FILE *));
-static char *skip_space PARAMS ((char *));
-static char *extract_word PARAMS ((char *, char *, int));
-static unsigned int avr_operand PARAMS ((struct avr_opcodes_s *,
- int, char *, char **));
-static unsigned int avr_operands PARAMS ((struct avr_opcodes_s *, char **));
-static unsigned int avr_get_constant PARAMS ((char *, int));
-static char *parse_exp PARAMS ((char *, expressionS *));
-static bfd_reloc_code_real_type avr_ldi_expression PARAMS ((expressionS *));
-
-#define EXP_MOD_NAME(i) exp_mod[i].name
-#define EXP_MOD_RELOC(i) exp_mod[i].reloc
-#define EXP_MOD_NEG_RELOC(i) exp_mod[i].neg_reloc
-#define HAVE_PM_P(i) exp_mod[i].have_pm
+#define EXP_MOD_NAME(i) exp_mod[i].name
+#define EXP_MOD_RELOC(i) exp_mod[i].reloc
+#define EXP_MOD_NEG_RELOC(i) exp_mod[i].neg_reloc
+#define HAVE_PM_P(i) exp_mod[i].have_pm
struct exp_mod_s
{
- char *name;
- bfd_reloc_code_real_type reloc;
- bfd_reloc_code_real_type neg_reloc;
- int have_pm;
+ const char * name;
+ bfd_reloc_code_real_type reloc;
+ bfd_reloc_code_real_type neg_reloc;
+ int have_pm;
};
static struct exp_mod_s exp_mod[] =
{"pm_hi8", BFD_RELOC_AVR_HI8_LDI_PM, BFD_RELOC_AVR_HI8_LDI_PM_NEG, 0},
{"lo8", BFD_RELOC_AVR_LO8_LDI, BFD_RELOC_AVR_LO8_LDI_NEG, 1},
{"pm_lo8", BFD_RELOC_AVR_LO8_LDI_PM, BFD_RELOC_AVR_LO8_LDI_PM_NEG, 0},
- {"hlo8", -BFD_RELOC_AVR_LO8_LDI, -BFD_RELOC_AVR_LO8_LDI_NEG, 0},
- {"hhi8", -BFD_RELOC_AVR_HI8_LDI, -BFD_RELOC_AVR_HI8_LDI_NEG, 0},
+ {"hlo8", BFD_RELOC_AVR_HH8_LDI, BFD_RELOC_AVR_HH8_LDI_NEG, 0},
+ {"hhi8", BFD_RELOC_AVR_MS8_LDI, BFD_RELOC_AVR_MS8_LDI_NEG, 0},
};
+/* A union used to store indices into the exp_mod[] array
+ in a hash table which expects void * data types. */
+typedef union
+{
+ void * ptr;
+ int index;
+} mod_index;
+
/* Opcode hash table. */
static struct hash_control *avr_hash;
/* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx). */
static struct hash_control *avr_mod_hash;
+/* Whether some opcode does not change SREG. */
+static struct hash_control *avr_no_sreg_hash;
+
+static const char* const avr_no_sreg[] =
+ {
+ /* Arithmetic */
+ "ldi", "swap", "mov", "movw",
+ /* Special instructions. I-Flag will be restored by RETI, and we don't
+ consider I-Flag as being clobbered when changed. */
+ "sei", "cli", "reti", "brie", "brid",
+ "nop", "wdr", "sleep",
+ /* Load / Store */
+ "ld", "ldd", "lds", "pop", "in", "lpm", "elpm",
+ "st", "std", "sts", "push", "out",
+ /* Jumps and Calls. Calls might call code that changes SREG.
+ GCC has to filter out ABI calls. The non-ABI transparent calls
+ must use [R]CALL and are filtered out now by not mentioning them. */
+ "rjmp", "jmp", "ijmp", "ret",
+ /* Skipping. Branches need SREG to be set, hence we regard them
+ as if they changed SREG and don't list them here. */
+ "sbrc", "sbrs", "sbic", "sbis", "cpse",
+ /* I/O Manipulation */
+ "sbi", "cbi",
+ /* Read-Modify-Write */
+ "lac", "las", "lat", "xch"
+ };
+
#define OPTION_MMCU 'm'
-#define OPTION_ALL_OPCODES (OPTION_MD_BASE + 1)
-#define OPTION_NO_SKIP_BUG (OPTION_MD_BASE + 2)
-#define OPTION_NO_WRAP (OPTION_MD_BASE + 3)
+enum options
+{
+ OPTION_ALL_OPCODES = OPTION_MD_BASE + 1,
+ OPTION_NO_SKIP_BUG,
+ OPTION_NO_WRAP,
+ OPTION_ISA_RMW,
+ OPTION_LINK_RELAX,
+ OPTION_NO_LINK_RELAX,
+ OPTION_HAVE_GCCISR
+};
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 },
+ { "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 }
};
/* Display nicely formatted list of known MCU names. */
static void
-show_mcu_list (stream)
- FILE *stream;
+show_mcu_list (FILE *stream)
{
int i, x;
}
static inline char *
-skip_space (s)
- char *s;
+skip_space (char *s)
{
while (*s == ' ' || *s == '\t')
++s;
static char *
extract_word (char *from, char *to, int limit)
{
- char *op_start;
char *op_end;
int size = 0;
*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)
}
int
-md_estimate_size_before_relax (fragp, seg)
- fragS *fragp ATTRIBUTE_UNUSED;
- asection *seg ATTRIBUTE_UNUSED;
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
{
abort ();
return 0;
}
void
-md_show_usage (stream)
- FILE *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"
- " 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"
- " (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);
}
static void
-avr_set_arch (dummy)
- int dummy ATTRIBUTE_UNUSED;
+avr_set_arch (int dummy ATTRIBUTE_UNUSED)
{
- char *str;
+ char str[20];
- str = (char *) alloca (20);
input_line_pointer = extract_word (input_line_pointer, str, 20);
md_parse_option (OPTION_MMCU, str);
bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
}
int
-md_parse_option (c, arg)
- int c;
- char *arg;
+md_parse_option (int c, const char *arg)
{
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)
- if (strcmp (mcu_types[i].name, s) == 0)
+ if (strcasecmp (mcu_types[i].name, arg) == 0)
break;
if (!mcu_types[i].name)
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);
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;
}
-symbolS *
-md_undefined_symbol (name)
- char *name ATTRIBUTE_UNUSED;
-{
- return 0;
-}
-/* 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 (type, litP, sizeP)
- 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;
}
+const char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
void
-md_convert_frag (abfd, sec, fragP)
- bfd *abfd ATTRIBUTE_UNUSED;
- asection *sec ATTRIBUTE_UNUSED;
- fragS *fragP ATTRIBUTE_UNUSED;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
{
abort ();
}
void
-md_begin ()
+md_begin (void)
{
unsigned int i;
struct avr_opcodes_s *opcode;
+
avr_hash = hash_new ();
/* Insert unique names into hash table. This hash table then provides a
avr_mod_hash = hash_new ();
- for (i = 0; i < sizeof (exp_mod) / sizeof (exp_mod[0]); ++i)
- hash_insert (avr_mod_hash, EXP_MOD_NAME (i), (void *) (i + 10));
+ for (i = 0; i < ARRAY_SIZE (exp_mod); ++i)
+ {
+ mod_index m;
+
+ m.index = i + 10;
+ hash_insert (avr_mod_hash, EXP_MOD_NAME (i), m.ptr);
+ }
+
+ avr_no_sreg_hash = hash_new ();
+
+ for (i = 0; i < ARRAY_SIZE (avr_no_sreg); ++i)
+ {
+ gas_assert (hash_find (avr_hash, avr_no_sreg[i]));
+ hash_insert (avr_no_sreg_hash, avr_no_sreg[i], (char*) 4 /* dummy */);
+ }
+
+ avr_gccisr_opcode = (struct avr_opcodes_s*) hash_find (avr_hash, "__gcc_isr");
+ gas_assert (avr_gccisr_opcode);
bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
+ linkrelax = !avr_opt.no_link_relax;
}
/* Resolve STR as a constant expression and return the result.
If result greater than MAX then error. */
static unsigned int
-avr_get_constant (str, max)
- char *str;
- int max;
+avr_get_constant (char *str, int max)
{
expressionS ex;
+
str = skip_space (str);
input_line_pointer = str;
- expression (&ex);
+ expression (& ex);
if (ex.X_op != O_constant)
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;
}
-/* Parse instruction operands.
- Return binary opcode. */
+/* Parse for ldd/std offset. */
-static unsigned int
-avr_operands (opcode, line)
- struct avr_opcodes_s *opcode;
- char **line;
+static void
+avr_offset_expression (expressionS *exp)
{
- 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;
- static unsigned int prev = 0; /* Previous opcode. */
-
- /* Opcode have operands. */
- if (*op)
- {
- unsigned int reg1 = 0;
- unsigned int reg2 = 0;
- int reg1_present = 0;
- int reg2_present = 0;
-
- /* Parse first operand. */
- if (REGISTER_P (*op))
- reg1_present = 1;
- reg1 = avr_operand (opcode, where, op, &str);
- ++op;
-
- /* Parse second operand. */
- if (*op)
- {
- if (*op == ',')
- ++op;
-
- if (*op == '=')
- {
- reg2 = reg1;
- reg2_present = 1;
- }
- else
- {
- if (REGISTER_P (*op))
- reg2_present = 1;
-
- str = skip_space (str);
- if (*str++ != ',')
- as_bad (_("`,' required"));
- str = skip_space (str);
-
- reg2 = avr_operand (opcode, where, op, &str);
-
- }
+ char *str = input_line_pointer;
+ char *tmp;
+ char op[8];
- if (reg1_present && reg2_present)
- reg2 = (reg2 & 0xf) | ((reg2 << 5) & 0x200);
- else if (reg2_present)
- reg2 <<= 4;
- }
- if (reg1_present)
- reg1 <<= 4;
- bin |= reg1 | reg2;
- }
+ tmp = str;
+ str = extract_word (str, op, sizeof (op));
- /* Detect undefined combinations (like ld r31,Z+). */
- if (!avr_opt.all_opcodes && AVR_UNDEF_P (bin))
- as_warn (_("undefined combination of operands"));
+ input_line_pointer = tmp;
+ expression (exp);
- if (opcode->insn_size == 2)
+ /* Warn about expressions that fail to use lo8 (). */
+ if (exp->X_op == O_constant)
{
- /* Warn if the previous opcode was cpse/sbic/sbis/sbrc/sbrs
- (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))
- as_warn (_("skipping two-word instruction"));
+ int x = exp->X_add_number;
- bfd_putl32 ((bfd_vma) bin, frag);
+ if (x < -255 || x > 255)
+ as_warn (_("constant out of 8-bit range: %d"), x);
}
- else
- bfd_putl16 ((bfd_vma) bin, frag);
+}
- prev = bin;
- *line = str;
- return bin;
+/* Parse ordinary expression. */
+
+static char *
+parse_exp (char *s, expressionS *op)
+{
+ input_line_pointer = s;
+ expression (op);
+ if (op->X_op == O_absent)
+ as_bad (_("missing operand"));
+ return input_line_pointer;
}
-/* Parse for ldd/std offset */
+/* Parse special expressions (needed for LDI command):
+ xx8 (address)
+ xx8 (-address)
+ pm_xx8 (address)
+ pm_xx8 (-address)
+ where xx is: hh, hi, lo. */
-static void
-avr_offset_expression (expressionS *exp)
+static bfd_reloc_code_real_type
+avr_ldi_expression (expressionS *exp)
{
char *str = input_line_pointer;
char *tmp;
char op[8];
+ int mod;
+ int linker_stubs_should_be_generated = 0;
tmp = str;
+
str = extract_word (str, op, sizeof (op));
+ if (op[0])
+ {
+ mod_index m;
+
+ m.ptr = hash_find (avr_mod_hash, op);
+ mod = m.index;
+
+ if (mod)
+ {
+ int closes = 0;
+
+ mod -= 10;
+ str = skip_space (str);
+
+ if (*str == '(')
+ {
+ bfd_reloc_code_real_type reloc_to_return;
+ int neg_p = 0;
+
+ ++str;
+
+ if (strncmp ("pm(", str, 3) == 0
+ || strncmp ("gs(",str,3) == 0
+ || strncmp ("-(gs(",str,5) == 0
+ || strncmp ("-(pm(", str, 5) == 0)
+ {
+ if (HAVE_PM_P (mod))
+ {
+ ++mod;
+ ++closes;
+ }
+ else
+ as_bad (_("illegal expression"));
+
+ if (str[0] == 'g' || str[2] == 'g')
+ linker_stubs_should_be_generated = 1;
+
+ if (*str == '-')
+ {
+ neg_p = 1;
+ ++closes;
+ str += 5;
+ }
+ else
+ str += 3;
+ }
+
+ if (*str == '-' && *(str + 1) == '(')
+ {
+ neg_p ^= 1;
+ ++closes;
+ str += 2;
+ }
+
+ input_line_pointer = str;
+ expression (exp);
+
+ do
+ {
+ if (*input_line_pointer != ')')
+ {
+ as_bad (_("`)' required"));
+ break;
+ }
+ input_line_pointer++;
+ }
+ while (closes--);
+
+ reloc_to_return =
+ neg_p ? EXP_MOD_NEG_RELOC (mod) : EXP_MOD_RELOC (mod);
+ if (linker_stubs_should_be_generated)
+ {
+ switch (reloc_to_return)
+ {
+ case BFD_RELOC_AVR_LO8_LDI_PM:
+ reloc_to_return = BFD_RELOC_AVR_LO8_LDI_GS;
+ break;
+ case BFD_RELOC_AVR_HI8_LDI_PM:
+ reloc_to_return = BFD_RELOC_AVR_HI8_LDI_GS;
+ break;
+
+ default:
+ /* PR 5523: Do not generate a warning here,
+ legitimate code can trigger this case. */
+ break;
+ }
+ }
+ return reloc_to_return;
+ }
+ }
+ }
+
input_line_pointer = tmp;
expression (exp);
if (exp->X_op == O_constant)
{
int x = exp->X_add_number;
-
+
if (x < -255 || x > 255)
as_warn (_("constant out of 8-bit range: %d"), x);
}
+
+ return BFD_RELOC_AVR_LDI;
}
/* Parse one instruction operand.
Return operand bitmask. Also fixups can be generated. */
static unsigned int
-avr_operand (opcode, where, op, line)
- struct avr_opcodes_s *opcode;
- int where;
- char *op;
- char **line;
+avr_operand (struct avr_opcodes_s *opcode,
+ int where,
+ const char *op,
+ char **line,
+ int *pregno)
{
expressionS op_expr;
unsigned int op_mask = 0;
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':
break;
}
break;
- }
- as_bad (_("register name or number from 0 to 31 required"));
- break;
case 'e':
{
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':
&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;
}
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);
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':
+ 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;
- x = avr_get_constant (str, 31);
+ x = avr_get_constant (str, 15);
str = input_line_pointer;
- op_mask |= x << 3;
+ op_mask |= (x << 4);
}
break;
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)
+{
+ 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;
+ int regno1 = -2;
+ int regno2 = -2;
+
+ /* Opcode have operands. */
+ if (*op)
+ {
+ unsigned int reg1 = 0;
+ unsigned int reg2 = 0;
+ int reg1_present = 0;
+ int reg2_present = 0;
+
+ /* Parse first operand. */
+ if (REGISTER_P (*op))
+ reg1_present = 1;
+ reg1 = avr_operand (opcode, where, op, &str, ®no1);
+ ++op;
+
+ /* Parse second operand. */
+ if (*op)
+ {
+ if (*op == ',')
+ ++op;
+
+ if (*op == '=')
+ {
+ reg2 = reg1;
+ reg2_present = 1;
+ regno2 = regno1;
+ }
+ else
+ {
+ if (REGISTER_P (*op))
+ reg2_present = 1;
+
+ str = skip_space (str);
+ if (*str++ != ',')
+ as_bad (_("`,' required"));
+ str = skip_space (str);
+
+ reg2 = avr_operand (opcode, where, op, &str, ®no2);
+ }
+
+ if (reg1_present && reg2_present)
+ reg2 = (reg2 & 0xf) | ((reg2 << 5) & 0x200);
+ else if (reg2_present)
+ reg2 <<= 4;
+ }
+ if (reg1_present)
+ reg1 <<= 4;
+ 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"));
+
+ if (opcode->insn_size == 2)
+ {
+ /* Warn if the previous opcode was cpse/sbic/sbis/sbrc/sbrs
+ (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 (frag_now->tc_frag_data.prev_opcode))
+ as_warn (_("skipping two-word instruction"));
+
+ bfd_putl32 ((bfd_vma) bin, frag);
+ }
+ else
+ bfd_putl16 ((bfd_vma) bin, frag);
+
+ frag_now->tc_frag_data.prev_opcode = bin;
+ *line = str;
+ return bin;
+}
+
/* GAS will call this function for each section at the end of the assembly,
to permit the CPU backend to adjust the alignment of a section. */
valueT
-md_section_align (seg, addr)
- asection *seg;
- valueT addr;
+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
macro would return the length of an instruction. */
long
-md_pcrel_from_section (fixp, sec)
- fixS *fixp;
- segT sec;
+md_pcrel_from_section (fixS *fixp, segT sec)
{
if (fixp->fx_addsy != (symbolS *) NULL
&& (!S_IS_DEFINED (fixp->fx_addsy)
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. */
void
-md_apply_fix3 (fixP, valP, seg)
- fixS *fixP;
- valueT * valP;
- segT seg;
+md_apply_fix (fixS *fixP, valueT * valP, segT seg)
{
unsigned char *where;
unsigned long insn;
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"));
+ /* 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:
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;
}
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;
+ 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;
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);
- 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:
bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where);
break;
- case -BFD_RELOC_AVR_LO8_LDI:
- bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 16), where);
- break;
-
case BFD_RELOC_AVR_HI8_LDI:
bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 8), where);
break;
- case -BFD_RELOC_AVR_HI8_LDI:
+ case BFD_RELOC_AVR_MS8_LDI:
bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 24), where);
break;
bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value), where);
break;
- case -BFD_RELOC_AVR_LO8_LDI_NEG:
- bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 16), where);
- break;
-
case BFD_RELOC_AVR_HI8_LDI_NEG:
bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 8), where);
break;
- case -BFD_RELOC_AVR_HI8_LDI_NEG:
+ case BFD_RELOC_AVR_MS8_LDI_NEG:
bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 24), where);
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;
+
+ 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
{
- switch (fixP->fx_r_type)
+ switch ((int) fixP->fx_r_type)
{
case -BFD_RELOC_AVR_HI8_LDI_NEG:
case -BFD_RELOC_AVR_HI8_LDI:
}
}
-/* A `BFD_ASSEMBLER' GAS will call this to generate a reloc. GAS
- will pass the resulting reloc to `bfd_install_relocation'. This
- currently works poorly, as `bfd_install_relocation' often does the
- wrong thing, and instances of `tc_gen_reloc' have been written to
- work around the problems, which in turns makes it difficult to fix
- `bfd_install_relocation'. */
+/* GAS will call this to generate a reloc, passing the resulting reloc
+ to `bfd_install_relocation'. This currently works poorly, as
+ `bfd_install_relocation' often does the wrong thing, and instances of
+ `tc_gen_reloc' have been written to work around the problems, which
+ in turns makes it difficult to fix `bfd_install_relocation'. */
/* If while processing a fixup, a reloc really needs to be created
then it is done here. */
arelent *
-tc_gen_reloc (seg, fixp)
- asection *seg ATTRIBUTE_UNUSED;
- fixS *fixp;
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED,
+ fixS *fixp)
{
arelent *reloc;
+ bfd_reloc_code_real_type code = fixp->fx_r_type;
+
+ if (fixp->fx_subsy != NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line, _("expression too complex"));
+ return NULL;
+ }
- reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc = XNEW (arelent);
- reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
- reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ 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->addend = fixp->fx_offset;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+
if (reloc->howto == (reloc_howto_type *) NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
|| fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
reloc->address = fixp->fx_offset;
- reloc->addend = fixp->fx_offset;
return reloc;
}
void
-md_assemble (str)
- char *str;
+md_assemble (char *str)
{
struct avr_opcodes_s *opcode;
char op[11];
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 == 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;
- 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;
- 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;
}
}
-/* Parse ordinary expression. */
-
-static char *
-parse_exp (s, op)
- char *s;
- expressionS *op;
+const exp_mod_data_t exp_mod_data[] =
{
- input_line_pointer = s;
- expression (op);
- if (op->X_op == O_absent)
- as_bad (_("missing operand"));
- return input_line_pointer;
-}
+ /* 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 expressions (needed for LDI command):
- xx8 (address)
- xx8 (-address)
- pm_xx8 (address)
- pm_xx8 (-address)
- where xx is: hh, hi, lo. */
+/* 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). */
-static bfd_reloc_code_real_type
-avr_ldi_expression (exp)
- expressionS *exp;
+const exp_mod_data_t *
+avr_parse_cons_expression (expressionS *exp, int nbytes)
{
- char *str = input_line_pointer;
char *tmp;
- char op[8];
- int mod;
- tmp = str;
+ unsigned int i;
- str = extract_word (str, op, sizeof (op));
+ tmp = input_line_pointer = skip_space (input_line_pointer);
- if (op[0])
+ /* 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++)
{
- mod = (int) hash_find (avr_mod_hash, op);
+ const exp_mod_data_t *pexp = &exp_mod_data[i];
+ int len = strlen (pexp->name);
- if (mod)
+ if (nbytes == pexp->nbytes
+ && strncasecmp (input_line_pointer, pexp->name, len) == 0)
{
- int closes = 0;
-
- mod -= 10;
- str = skip_space (str);
+ input_line_pointer = skip_space (input_line_pointer + len);
- if (*str == '(')
+ if (*input_line_pointer == '(')
{
- int neg_p = 0;
-
- ++str;
+ input_line_pointer = skip_space (input_line_pointer + 1);
+ expression (exp);
- if (strncmp ("pm(", str, 3) == 0
- || strncmp ("-(pm(", str, 5) == 0)
+ if (*input_line_pointer == ')')
{
- if (HAVE_PM_P (mod))
- {
- ++mod;
- ++closes;
- }
- else
- as_bad (_("illegal expression"));
-
- if (*str == '-')
- {
- neg_p = 1;
- ++closes;
- str += 5;
- }
- else
- str += 3;
+ ++input_line_pointer;
+ return pexp;
}
-
- if (*str == '-' && *(str + 1) == '(')
+ else
{
- neg_p ^= 1;
- ++closes;
- str += 2;
+ as_bad (_("`)' required"));
+ return &exp_mod_data[0];
}
+ }
- input_line_pointer = str;
- expression (exp);
-
- do
- {
- if (*input_line_pointer != ')')
- {
- as_bad (_("`)' required"));
- break;
- }
- input_line_pointer++;
- }
- while (closes--);
+ input_line_pointer = tmp;
- return neg_p ? EXP_MOD_NEG_RELOC (mod) : EXP_MOD_RELOC (mod);
- }
+ break;
}
}
- input_line_pointer = tmp;
expression (exp);
+ return &exp_mod_data[0];
+}
- /* Warn about expressions that fail to use lo8 (). */
- if (exp->X_op == O_constant)
+void
+avr_cons_fix_new (fragS *frag,
+ int where,
+ int nbytes,
+ expressionS *exp,
+ const exp_mod_data_t *pexp_mod_data)
+{
+ int bad = 0;
+
+ switch (pexp_mod_data->reloc)
{
- int x = exp->X_add_number;
- if (x < -255 || x > 255)
- as_warn (_("constant out of 8-bit range: %d"), x);
+ 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
+ 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;
}
- return BFD_RELOC_AVR_LDI;
+ if (bad)
+ as_bad (_("illegal %s relocation size: %d"), pexp_mod_data->error, nbytes);
}
-/* Flag to pass `pm' mode between `avr_parse_cons_expression' and
- `avr_cons_fix_new'. */
-static int exp_mod_pm = 0;
+static bfd_boolean
+mcu_has_3_byte_pc (void)
+{
+ int mach = avr_mcu->mach;
-/* Parse special CONS expression: pm (expression)
- which is used for addressing to a program memory.
- Relocation: BFD_RELOC_AVR_16_PM. */
+ return mach == bfd_mach_avr6
+ || mach == bfd_mach_avrxmega6
+ || mach == bfd_mach_avrxmega7;
+}
void
-avr_parse_cons_expression (exp, nbytes)
- expressionS *exp;
- int nbytes;
+tc_cfi_frame_initial_instructions (void)
{
- char *tmp;
+ /* AVR6 pushes 3 bytes for calls. */
+ int return_size = (mcu_has_3_byte_pc () ? 3 : 2);
- exp_mod_pm = 0;
+ /* 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);
- tmp = input_line_pointer = skip_space (input_line_pointer);
+ /* 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;
- if (nbytes == 2)
+ 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)
{
- char *pm_name = "pm";
- int len = strlen (pm_name);
+ /* 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 (strncasecmp (input_line_pointer, pm_name, len) == 0)
+ 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)
{
- input_line_pointer = skip_space (input_line_pointer + len);
+ 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;
+ }
+ }
+ }
- if (*input_line_pointer == '(')
+ 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
+ abort();
+
+ if (strchr (op->constraints, 'P'))
+ {
+ bin |= ((sreg & 0x30) << 5) | (sreg & 0x0f);
+ }
+ else if (0 == strcmp ("r=r", op->constraints))
+ {
+ bin |= ((reg & 0x10) << 5) | (reg & 0x0f);
+ }
+ else
+ gas_assert (0 == strcmp ("r", op->constraints)
+ || 0 == strcmp ("ldi", op->name));
+
+ bfd_putl16 ((bfd_vma) bin, *pwhere);
+ (*pwhere) += 2 * op->insn_size;
+}
+
+
+/* Turn rs_machine_dependent frag *FR into an ordinary rs_fill code frag,
+ using information gathered in `avr_isr'. REG is the register number as
+ supplied by Done chunk "__gcc_isr 0,REG". */
+
+static void
+avr_patch_gccisr_frag (fragS *fr, int reg)
+{
+ int treg;
+ int n_pushed = 0;
+ char *where = fr->fr_literal;
+ const int tiny_p = avr_mcu->mach == bfd_mach_avrtiny;
+ const int reg_tmp = tiny_p ? 16 : 0;
+ const int reg_zero = 1 + reg_tmp;
+
+ /* Clearing ZERO_REG on non-Tiny needs CLR which clobbers SREG. */
+
+ avr_isr.need_sreg |= !tiny_p && avr_isr.need_reg_zero;
+
+ /* A working register to PUSH / POP the SREG. We might use the register
+ as supplied by ISR_CHUNK_Done for that purpose as GCC wants to push
+ it anyways. If GCC passes ZERO_REG or TMP_REG, it has no clue (and
+ no additional regs to safe) and we use that reg. */
+
+ treg
+ = avr_isr.need_reg_tmp ? reg_tmp
+ : avr_isr.need_reg_zero ? reg_zero
+ : avr_isr.need_sreg ? reg
+ : reg > reg_zero ? reg
+ : -1;
+
+ if (treg >= 0)
+ {
+ /* Non-empty prologue / epilogue */
+
+ if (ISR_CHUNK_Prologue == fr->fr_subtype)
+ {
+ avr_emit_insn ("push", treg, &where);
+ n_pushed++;
+
+ if (avr_isr.need_sreg)
{
- input_line_pointer = skip_space (input_line_pointer + 1);
- exp_mod_pm = 1;
- expression (exp);
+ avr_emit_insn ("in", treg, &where);
+ avr_emit_insn ("push", treg, &where);
+ n_pushed++;
+ }
- if (*input_line_pointer == ')')
- ++input_line_pointer;
- else
+ if (avr_isr.need_reg_zero)
+ {
+ if (reg_zero != treg)
{
- as_bad (_("`)' required"));
- exp_mod_pm = 0;
+ 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);
- return;
+ if (avr_isr.need_sreg)
+ {
+ avr_emit_insn ("pop", treg, &where);
+ avr_emit_insn ("out", treg, &where);
}
- input_line_pointer = tmp;
+ avr_emit_insn ("pop", treg, &where);
}
+ else
+ abort();
+ } /* treg >= 0 */
+
+ if (ISR_CHUNK_Prologue == fr->fr_subtype
+ && avr_isr.sym_n_pushed)
+ {
+ symbolS *sy = avr_isr.sym_n_pushed;
+ /* Turn magic `__gcc_isr.n_pushed' into its now known value. */
+
+ S_SET_VALUE (sy, n_pushed);
+ S_SET_SEGMENT (sy, expr_section);
+ avr_isr.sym_n_pushed = NULL;
}
- expression (exp);
+ /* 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;
}
-void
-avr_cons_fix_new (frag, where, nbytes, exp)
- fragS *frag;
- int where;
- int nbytes;
- expressionS *exp;
+
+/* 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)
{
- if (exp_mod_pm == 0)
+ 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)
{
- 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);
+ /* 2nd operand: A register to push / pop. */
+
+ str = skip_space (str);
+ if (*str == '\0' || *str++ != ',')
+ as_bad (_("`,' required"));
else
- as_bad (_("illegal %srelocation size: %d"), "", nbytes);
+ avr_operand (opcode, -1, "r", &str, ®);
}
- else
+
+ *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 (nbytes == 2)
- fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_AVR_16_PM);
+ 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 (_("illegal %srelocation size: %d"), "`pm' ", nbytes);
- exp_mod_pm = 0;
+ 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);
}