X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-avr.c;h=36262ad8c0dceea43f49bac691c7217e63c83d14;hb=aa989b27d0bad451455416953c0e5026e229863a;hp=64382e679d6b74970944f08c46230a9e6d75987a;hpb=7f5ba16ddb4b994fbc29ea3400f4b73236a54642;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-avr.c b/gas/config/tc-avr.c index 64382e679d..36262ad8c0 100644 --- a/gas/config/tc-avr.c +++ b/gas/config/tc-avr.c @@ -1,7 +1,6 @@ /* tc-avr.c -- Assembler code for the ATMEL AVR - Copyright 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 - Free Software Foundation, Inc. + Copyright (C) 1999-2019 Free Software Foundation, Inc. Contributed by Denis Chertykov This file is part of GAS, the GNU Assembler. @@ -24,25 +23,138 @@ #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; + 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[] = "$"; @@ -50,122 +162,305 @@ 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}, - {"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}, - {"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}, - {"attiny43u", AVR_ISA_TINY2, bfd_mach_avr2}, - {"attiny48", AVR_ISA_TINY2, bfd_mach_avr2}, - {"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}, - {"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}, - {"atmega8hva", AVR_ISA_PWMx, bfd_mach_avr4}, - {"at90pwm1", AVR_ISA_PWMx, bfd_mach_avr4}, - {"at90pwm2", AVR_ISA_PWMx, bfd_mach_avr4}, - {"at90pwm2b", AVR_ISA_PWMx, bfd_mach_avr4}, - {"at90pwm3", AVR_ISA_PWMx, bfd_mach_avr4}, - {"at90pwm3b", 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}, - {"atmega162", AVR_ISA_M323, bfd_mach_avr5}, + {"atmega162", AVR_ISA_AVR5, 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}, - {"atmega16hva",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}, - {"at90pwm216", AVR_ISA_M323, bfd_mach_avr5}, - {"at90pwm316", AVR_ISA_M323, bfd_mach_avr5}, - {"at90usb82", AVR_ISA_M323, 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}, - {"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} }; + /* 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. */ @@ -174,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 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"; @@ -199,7 +497,7 @@ const pseudo_typeS md_pseudo_table[] = 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; @@ -217,7 +515,7 @@ static struct exp_mod_s exp_mod[] = {"hhi8", BFD_RELOC_AVR_MS8_LDI, BFD_RELOC_AVR_MS8_LDI_NEG, 0}, }; -/* A union used to store indicies into the exp_mod[] array +/* A union used to store indices into the exp_mod[] array in a hash table which expects void * data types. */ typedef union { @@ -231,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; +/* 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, - OPTION_NO_WRAP + OPTION_NO_WRAP, + OPTION_ISA_RMW, + OPTION_LINK_RELAX, + OPTION_NO_LINK_RELAX, + OPTION_HAVE_GCCISR }; struct option md_longopts[] = @@ -245,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 }, + { "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 } }; @@ -291,7 +624,6 @@ skip_space (char *s) static char * extract_word (char *from, char *to, int limit) { - char *op_start; char *op_end; int size = 0; @@ -300,7 +632,7 @@ extract_word (char *from, char *to, int limit) *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) @@ -323,21 +655,39 @@ void 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\n" - " avr4 - ATmega8, ATmega88\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); } @@ -352,26 +702,16 @@ avr_set_arch (int dummy ATTRIBUTE_UNUSED) } int -md_parse_option (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) @@ -384,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) - 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); @@ -399,18 +744,51 @@ md_parse_option (int c, char *arg) 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; } + +/* Implement `md_undefined_symbol' */ +/* If we are in `__gcc_isr' chunk, pop up `__gcc_isr.n_pushed.' + instead of `__gcc_isr.n_pushed'. This will be resolved by the Done + chunk in `avr_patch_gccisr_frag' to the number of PUSHes produced by + the Prologue chunk. */ + symbolS * -md_undefined_symbol (char *name ATTRIBUTE_UNUSED) +avr_undefined_symbol (char *name) { + if (ISR_CHUNK_Done != avr_isr.prev_chunk + && 0 == strcmp (name, "__gcc_isr.n_pushed")) + { + if (!avr_isr.sym_n_pushed) + { + static unsigned suffix; + char xname[30]; + sprintf (xname, "%s.%03u", name, (++suffix) % 1000); + avr_isr.sym_n_pushed = symbol_new (xname, undefined_section, + (valueT) 0, &zero_address_frag); + } + return avr_isr.sym_n_pushed; + } + return NULL; } -char * +const char * md_atof (int type, char *litP, int *sizeP) { return ieee_md_atof (type, litP, sizeP, FALSE); @@ -448,7 +826,19 @@ md_begin (void) hash_insert (avr_mod_hash, EXP_MOD_NAME (i), m.ptr); } + avr_no_sreg_hash = hash_new (); + + for (i = 0; i < ARRAY_SIZE (avr_no_sreg); ++i) + { + gas_assert (hash_find (avr_hash, avr_no_sreg[i])); + hash_insert (avr_no_sreg_hash, avr_no_sreg[i], (char*) 4 /* dummy */); + } + + avr_gccisr_opcode = (struct avr_opcodes_s*) hash_find (avr_hash, "__gcc_isr"); + gas_assert (avr_gccisr_opcode); + bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach); + linkrelax = !avr_opt.no_link_relax; } /* Resolve STR as a constant expression and return the result. @@ -611,7 +1001,9 @@ avr_ldi_expression (expressionS *exp) 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; @@ -640,8 +1032,9 @@ avr_ldi_expression (expressionS *exp) 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; @@ -655,30 +1048,58 @@ avr_operand (struct avr_opcodes_s *opcode, 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': @@ -706,9 +1127,6 @@ avr_operand (struct avr_opcodes_s *opcode, break; } break; - } - as_bad (_("register name or number from 0 to 31 required")); - break; case 'e': { @@ -756,8 +1174,19 @@ avr_operand (struct avr_opcodes_s *opcode, 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': @@ -804,6 +1233,12 @@ avr_operand (struct avr_opcodes_s *opcode, &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; @@ -826,6 +1261,16 @@ avr_operand (struct avr_opcodes_s *opcode, } break; + case 'N': + { + unsigned int x; + + x = avr_get_constant (str, 255); + str = input_line_pointer; + op_mask = x; + } + break; + case 'K': input_line_pointer = str; avr_offset_expression (& op_expr); @@ -848,22 +1293,24 @@ avr_operand (struct avr_opcodes_s *opcode, 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; @@ -878,18 +1325,28 @@ avr_operand (struct avr_opcodes_s *opcode, return op_mask; } +/* TC_FRAG_INIT hook */ + +void +avr_frag_init (fragS *frag) +{ + memset (& frag->tc_frag_data, 0, sizeof frag->tc_frag_data); +} + + /* Parse instruction operands. Return binary opcode. */ 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; - static unsigned int prev = 0; /* Previous opcode. */ + int regno1 = -2; + int regno2 = -2; /* Opcode have operands. */ if (*op) @@ -902,7 +1359,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line) /* Parse first operand. */ if (REGISTER_P (*op)) reg1_present = 1; - reg1 = avr_operand (opcode, where, op, &str); + reg1 = avr_operand (opcode, where, op, &str, ®no1); ++op; /* Parse second operand. */ @@ -915,6 +1372,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line) { reg2 = reg1; reg2_present = 1; + regno2 = regno1; } else { @@ -926,7 +1384,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line) as_bad (_("`,' required")); str = skip_space (str); - reg2 = avr_operand (opcode, where, op, &str); + reg2 = avr_operand (opcode, where, op, &str, ®no2); } if (reg1_present && reg2_present) @@ -939,6 +1397,9 @@ avr_operands (struct avr_opcodes_s *opcode, char **line) bin |= reg1 | reg2; } + if (avr_opt.have_gccisr) + avr_update_gccisr (opcode, regno1, regno2); + /* Detect undefined combinations (like ld r31,Z+). */ if (!avr_opt.all_opcodes && AVR_UNDEF_P (bin)) as_warn (_("undefined combination of operands")); @@ -949,7 +1410,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line) (AVR core bug, fixed in the newer devices). */ if (!(avr_opt.no_skip_bug || (avr_mcu->isa & (AVR_ISA_MUL | AVR_ISA_MOVW))) - && AVR_SKIP_P (prev)) + && AVR_SKIP_P (frag_now->tc_frag_data.prev_opcode)) as_warn (_("skipping two-word instruction")); bfd_putl32 ((bfd_vma) bin, frag); @@ -957,7 +1418,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line) else bfd_putl16 ((bfd_vma) bin, frag); - prev = bin; + frag_now->tc_frag_data.prev_opcode = bin; *line = str; return bin; } @@ -968,8 +1429,8 @@ avr_operands (struct avr_opcodes_s *opcode, char **line) valueT md_section_align (asection *seg, valueT addr) { - int align = bfd_get_section_alignment (stdoutput, seg); - 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 @@ -989,6 +1450,55 @@ md_pcrel_from_section (fixS *fixp, segT sec) 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. */ @@ -1012,11 +1522,48 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg) 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: @@ -1026,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: + 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; } @@ -1077,13 +1634,20 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg) 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; @@ -1095,11 +1659,21 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg) 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: @@ -1180,15 +1754,41 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg) } 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: @@ -1218,40 +1818,35 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, 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, @@ -1264,7 +1859,6 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) reloc->address = fixp->fx_offset; - reloc->addend = fixp->fx_offset; return reloc; } @@ -1282,108 +1876,935 @@ md_assemble (char *str) 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; } } -/* 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; - - exp_mod_pm = 0; + unsigned int i; 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); - exp_mod_pm = 1; expression (exp); if (*input_line_pointer == ')') - ++input_line_pointer; + { + ++input_line_pointer; + return pexp; + } else { as_bad (_("`)' required")); - exp_mod_pm = 0; + return &exp_mod_data[0]; } - - return; } input_line_pointer = tmp; + + break; } } expression (exp); + return &exp_mod_data[0]; } 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 - 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 = ""; + 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 + 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 - 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, ®); + } + + *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); +}