Add support for a __gcc_isr pseudo isntruction to the AVR assembler.
PR gas/21683 include * opcode/avr.h (AVR_INSN): Add one for __gcc_isr. gas * doc/c-avr.texi (AVR Options) <-mgcc-isr>: Document it. (AVR Pseudo Instructions): New node. * config/tc-avr.h (md_pre_output_hook): Define to avr_pre_output_hook. (md_undefined_symbol): Define to avr_undefined_symbol. (avr_pre_output_hook, avr_undefined_symbol): New protos. * config/tc-avr.c (struc-symbol.h): Include it. (ISR_CHUNK_Done, ISR_CHUNK_Prologue, ISR_CHUNK_Epilogue): New enums. (avr_isr, avr_gccisr_opcode) (avr_no_sreg_hash, avr_no_sreg): New static variables. (avr_opt_s) <have_gccisr>: Add field. (avr_opt): Add initializer for have_gccisr. (enum options) <OPTION_HAVE_GCCISR>: Add enum. (md_longopts) <"mgcc-isr">: Add entry. (md_show_usage): Document -mgcc-isr. (md_parse_option) [OPTION_HAVE_GCCISR]: Handle it. (md_undefined_symbol): Remove. (avr_undefined_symbol, avr_pre_output_hook): New fuctions. (md_begin) <avr_no_sreg_hash, avr_gccisr_opcode>: Initialize them. (avr_operand) <pregno>: Add argument and set *pregno if function is called for a register constraint. [N]: Handle constraint. (avr_operands) <avr_operand>: Pass 5th parameter to calls. [avr_opt.have_gccisr]: Call avr_update_gccisr. Call avr_gccisr_operands instead of avr_operands. (avr_update_gccisr, avr_emit_insn, avr_patch_gccisr_frag) (avr_gccisr_operands, avr_check_gccisr_done): New static functions. * testsuite/gas/avr/gccisr-01.d: New test. * testsuite/gas/avr/gccisr-01.s: New test. * testsuite/gas/avr/gccisr-02.d: New test. * testsuite/gas/avr/gccisr-02.s: New test. * testsuite/gas/avr/gccisr-03.d: New test. * testsuite/gas/avr/gccisr-03.s: New test.
This commit is contained in:
parent
33f466961c
commit
32f76c6773
@ -1,3 +1,39 @@
|
||||
2017-06-30 Georg-Johann Lay <avr@gjlay.de>
|
||||
|
||||
PR gas/21683
|
||||
* doc/c-avr.texi (AVR Options) <-mgcc-isr>: Document it.
|
||||
(AVR Pseudo Instructions): New node.
|
||||
* config/tc-avr.h (md_pre_output_hook): Define to avr_pre_output_hook.
|
||||
(md_undefined_symbol): Define to avr_undefined_symbol.
|
||||
(avr_pre_output_hook, avr_undefined_symbol): New protos.
|
||||
* config/tc-avr.c (struc-symbol.h): Include it.
|
||||
(ISR_CHUNK_Done, ISR_CHUNK_Prologue, ISR_CHUNK_Epilogue): New enums.
|
||||
(avr_isr, avr_gccisr_opcode)
|
||||
(avr_no_sreg_hash, avr_no_sreg): New static variables.
|
||||
(avr_opt_s) <have_gccisr>: Add field.
|
||||
(avr_opt): Add initializer for have_gccisr.
|
||||
(enum options) <OPTION_HAVE_GCCISR>: Add enum.
|
||||
(md_longopts) <"mgcc-isr">: Add entry.
|
||||
(md_show_usage): Document -mgcc-isr.
|
||||
(md_parse_option) [OPTION_HAVE_GCCISR]: Handle it.
|
||||
(md_undefined_symbol): Remove.
|
||||
(avr_undefined_symbol, avr_pre_output_hook): New fuctions.
|
||||
(md_begin) <avr_no_sreg_hash, avr_gccisr_opcode>: Initialize them.
|
||||
(avr_operand) <pregno>: Add argument and set *pregno if function
|
||||
is called for a register constraint.
|
||||
[N]: Handle constraint.
|
||||
(avr_operands) <avr_operand>: Pass 5th parameter to calls.
|
||||
[avr_opt.have_gccisr]: Call avr_update_gccisr. Call
|
||||
avr_gccisr_operands instead of avr_operands.
|
||||
(avr_update_gccisr, avr_emit_insn, avr_patch_gccisr_frag)
|
||||
(avr_gccisr_operands, avr_check_gccisr_done): New static functions.
|
||||
* testsuite/gas/avr/gccisr-01.d: New test.
|
||||
* testsuite/gas/avr/gccisr-01.s: New test.
|
||||
* testsuite/gas/avr/gccisr-02.d: New test.
|
||||
* testsuite/gas/avr/gccisr-02.s: New test.
|
||||
* testsuite/gas/avr/gccisr-03.d: New test.
|
||||
* testsuite/gas/avr/gccisr-03.s: New test.
|
||||
|
||||
2017-06-30 Maciej W. Rozycki <macro@imgtec.com>
|
||||
|
||||
* config/tc-mips.c (match_float_constant): Update description.
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "as.h"
|
||||
#include "safe-ctype.h"
|
||||
#include "subsegs.h"
|
||||
#include "struc-symbol.h"
|
||||
#include "dwarf2dbg.h"
|
||||
#include "dw2gencfi.h"
|
||||
#include "elf/avr.h"
|
||||
@ -54,6 +55,107 @@ struct avr_opcodes_s avr_opcodes[] =
|
||||
{NULL, NULL, NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
/* Stuff for the `__gcc_isr' pseudo instruction.
|
||||
|
||||
Purpose of the pseudo instruction is to emit more efficient ISR prologues
|
||||
and epilogues than GCC currently does. GCC has no explicit (on RTL level)
|
||||
modelling of SREG, TMP_REG or ZERO_REG. These regs are used implicitly
|
||||
during instruction printing. That doesn't hurt too much for ordinary
|
||||
functions, however for small ISRs there might be some overhead.
|
||||
|
||||
As implementing http://gcc.gnu.org/PR20296 would imply an almost complete
|
||||
rewite of GCC's AVR back-end (which might pop up less optimized code in
|
||||
other places), we provide a pseudo-instruction which is resolved by GAS
|
||||
into ISR prologue / epilogue as expected by GCC.
|
||||
|
||||
Using GAS for this purpose has the additional benefit that it can scan
|
||||
code emit by inline asm which is opaque to GCC.
|
||||
|
||||
The pseudo-instruction is only supposed to handle the starting of
|
||||
prologue and the ending of epilogues (without RETI) which deal with
|
||||
SREG, TMP_REG and ZERO_REG and one additional, optional general purpose
|
||||
register.
|
||||
|
||||
__gcc_isr consists of 3 different "chunks":
|
||||
|
||||
__gcc_isr 1
|
||||
Chunk 1 (ISR_CHUNK_Prologue)
|
||||
Start the ISR code. Will be replaced by ISR prologue by next Done chunk.
|
||||
Must be the 1st chunk in a file or follow a Done chunk from previous
|
||||
ISR (which has been patched already).
|
||||
|
||||
It will finish the current frag and emit a new frag of
|
||||
type rs_machine_dependent, subtype ISR_CHUNK_Prologue.
|
||||
|
||||
__gcc_isr 2
|
||||
Chunk 2 (ISR_CHUNK_Epilogue)
|
||||
Will be replaced by ISR epilogue by next Done chunk. Must follow
|
||||
chunk 1 (Prologue) or chunk 2 (Epilogue). Functions might come
|
||||
without epilogue or with more than one epilogue, and even code
|
||||
located statically after the last epilogue might belong to a function.
|
||||
|
||||
It will finish the current frag and emit a new frag of
|
||||
type rs_machine_dependent, subtype ISR_CHUNK_Epilogue.
|
||||
|
||||
__gcc_isr 0, Rx
|
||||
Chunk 0 (ISR_CHUNK_Done)
|
||||
Must follow chunk 1 (Prologue) or chunk 2 (Epilogue) and finishes
|
||||
the ISR code. Only GCC can know where a function's code ends.
|
||||
|
||||
It triggers the patch-up of all rs_machine_dependent frags in the
|
||||
current frag chain and turns them into ordinary rs_fill code frags.
|
||||
|
||||
If Rx is a register > ZERO_REG then GCC also wants to push / pop Rx.
|
||||
If neither TMP_REG nor ZERO_REG are needed, Rx will be used in
|
||||
the push / pop sequence avoiding the need for TMP_REG / ZERO_REG.
|
||||
If Rx <= ZERO_REG then GCC doesn't assume anything about Rx.
|
||||
|
||||
Assumptions:
|
||||
|
||||
o GCC takes care of code that is opaque to GAS like tail calls
|
||||
or non-local goto.
|
||||
|
||||
o Using SEI / CLI does not count as clobbering SREG. This is
|
||||
because a final RETI will restore the I-flag.
|
||||
|
||||
o Using OUT or ST* is supposed not to clobber SREG. Sequences like
|
||||
|
||||
IN-SREG + CLI + Atomic-Code + OUT-SREG
|
||||
|
||||
will still work as expected because the scan will reveal any
|
||||
clobber of SREG other than I-flag and emit PUSH / POP of SREG.
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
ISR_CHUNK_Done = 0,
|
||||
ISR_CHUNK_Prologue = 1,
|
||||
ISR_CHUNK_Epilogue = 2
|
||||
};
|
||||
|
||||
static struct
|
||||
{
|
||||
/* Previous __gcc_isr chunk (one of the enums above)
|
||||
and it's location for diagnostics. */
|
||||
int prev_chunk;
|
||||
unsigned line;
|
||||
const char *file;
|
||||
/* Replacer for __gcc_isr.n_pushed once we know how many regs are
|
||||
pushed by the Prologue chunk. */
|
||||
symbolS *sym_n_pushed;
|
||||
|
||||
/* Set and used during parse from chunk 1 (Prologue) up to chunk 0 (Done).
|
||||
Set by `avr_update_gccisr' and used by `avr_patch_gccisr_frag'. */
|
||||
int need_reg_tmp;
|
||||
int need_reg_zero;
|
||||
int need_sreg;
|
||||
} avr_isr;
|
||||
|
||||
static void avr_gccisr_operands (struct avr_opcodes_s*, char**);
|
||||
static void avr_update_gccisr (struct avr_opcodes_s*, int, int);
|
||||
static struct avr_opcodes_s *avr_gccisr_opcode;
|
||||
|
||||
const char comment_chars[] = ";";
|
||||
const char line_comment_chars[] = "#";
|
||||
const char line_separator_chars[] = "$";
|
||||
@ -359,9 +461,10 @@ struct avr_opt_s
|
||||
int no_wrap; /* -mno-wrap: reject rjmp/rcall with 8K wrap-around. */
|
||||
int no_link_relax; /* -mno-link-relax / -mlink-relax: generate (or not)
|
||||
relocations for linker relaxation. */
|
||||
int have_gccisr; /* Whether "__gcc_isr" is a known (pseudo) insn. */
|
||||
};
|
||||
|
||||
static struct avr_opt_s avr_opt = { 0, 0, 0, 0 };
|
||||
static struct avr_opt_s avr_opt = { 0, 0, 0, 0, 0 };
|
||||
|
||||
const char EXP_CHARS[] = "eE";
|
||||
const char FLT_CHARS[] = "dD";
|
||||
@ -416,6 +519,33 @@ static struct hash_control *avr_hash;
|
||||
/* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx). */
|
||||
static struct hash_control *avr_mod_hash;
|
||||
|
||||
/* Whether some opcode does not change SREG. */
|
||||
static struct hash_control *avr_no_sreg_hash;
|
||||
|
||||
static const char* const avr_no_sreg[] =
|
||||
{
|
||||
/* Arithmetic */
|
||||
"ldi", "swap", "mov", "movw",
|
||||
/* Special instructions. I-Flag will be restored by RETI, and we don't
|
||||
consider I-Flag as being clobbered when changed. */
|
||||
"sei", "cli", "reti", "brie", "brid",
|
||||
"nop", "wdr", "sleep",
|
||||
/* Load / Store */
|
||||
"ld", "ldd", "lds", "pop", "in", "lpm", "elpm",
|
||||
"st", "std", "sts", "push", "out",
|
||||
/* Jumps and Calls. Calls might call code that changes SREG.
|
||||
GCC has to filter out ABI calls. The non-ABI transparent calls
|
||||
must use [R]CALL and are filtered out now by not mentioning them. */
|
||||
"rjmp", "jmp", "ijmp", "ret",
|
||||
/* Skipping. Branches need SREG to be set, hence we regard them
|
||||
as if they changed SREG and don't list them here. */
|
||||
"sbrc", "sbrs", "sbic", "sbis", "cpse",
|
||||
/* I/O Manipulation */
|
||||
"sbi", "cbi",
|
||||
/* Read-Modify-Write */
|
||||
"lac", "las", "lat", "xch"
|
||||
};
|
||||
|
||||
#define OPTION_MMCU 'm'
|
||||
enum options
|
||||
{
|
||||
@ -424,7 +554,8 @@ enum options
|
||||
OPTION_NO_WRAP,
|
||||
OPTION_ISA_RMW,
|
||||
OPTION_LINK_RELAX,
|
||||
OPTION_NO_LINK_RELAX
|
||||
OPTION_NO_LINK_RELAX,
|
||||
OPTION_HAVE_GCCISR
|
||||
};
|
||||
|
||||
struct option md_longopts[] =
|
||||
@ -436,6 +567,7 @@ struct option md_longopts[] =
|
||||
{ "mrmw", no_argument, NULL, OPTION_ISA_RMW },
|
||||
{ "mlink-relax", no_argument, NULL, OPTION_LINK_RELAX },
|
||||
{ "mno-link-relax", no_argument, NULL, OPTION_NO_LINK_RELAX },
|
||||
{ "mgcc-isr", no_argument, NULL, OPTION_HAVE_GCCISR },
|
||||
{ NULL, no_argument, NULL, 0 }
|
||||
};
|
||||
|
||||
@ -544,6 +676,7 @@ md_show_usage (FILE *stream)
|
||||
" -mrmw accept Read-Modify-Write instructions\n"
|
||||
" -mlink-relax generate relocations for linker relaxation (default)\n"
|
||||
" -mno-link-relax don't generate relocations for linker relaxation.\n"
|
||||
" -mgcc-isr accept the __gcc_isr pseudo-instruction.\n"
|
||||
));
|
||||
show_mcu_list (stream);
|
||||
}
|
||||
@ -610,14 +743,38 @@ md_parse_option (int c, const char *arg)
|
||||
case OPTION_NO_LINK_RELAX:
|
||||
avr_opt.no_link_relax = 1;
|
||||
return 1;
|
||||
case OPTION_HAVE_GCCISR:
|
||||
avr_opt.have_gccisr = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Implement `md_undefined_symbol' */
|
||||
/* If we are in `__gcc_isr' chunk, pop up `__gcc_isr.n_pushed.<NUM>'
|
||||
instead of `__gcc_isr.n_pushed'. This will be resolved by the Done
|
||||
chunk in `avr_patch_gccisr_frag' to the number of PUSHes produced by
|
||||
the Prologue chunk. */
|
||||
|
||||
symbolS *
|
||||
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
|
||||
avr_undefined_symbol (char *name)
|
||||
{
|
||||
if (ISR_CHUNK_Done != avr_isr.prev_chunk
|
||||
&& 0 == strcmp (name, "__gcc_isr.n_pushed"))
|
||||
{
|
||||
if (!avr_isr.sym_n_pushed)
|
||||
{
|
||||
static unsigned suffix;
|
||||
char xname[30];
|
||||
sprintf (xname, "%s.%03u", name, (++suffix) % 1000);
|
||||
avr_isr.sym_n_pushed = symbol_new (xname, undefined_section,
|
||||
(valueT) 0, &zero_address_frag);
|
||||
}
|
||||
return avr_isr.sym_n_pushed;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -659,6 +816,17 @@ md_begin (void)
|
||||
hash_insert (avr_mod_hash, EXP_MOD_NAME (i), m.ptr);
|
||||
}
|
||||
|
||||
avr_no_sreg_hash = hash_new ();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE (avr_no_sreg); ++i)
|
||||
{
|
||||
gas_assert (hash_find (avr_hash, avr_no_sreg[i]));
|
||||
hash_insert (avr_no_sreg_hash, avr_no_sreg[i], (char*) 4 /* dummy */);
|
||||
}
|
||||
|
||||
avr_gccisr_opcode = (struct avr_opcodes_s*) hash_find (avr_hash, "__gcc_isr");
|
||||
gas_assert (avr_gccisr_opcode);
|
||||
|
||||
bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
|
||||
linkrelax = !avr_opt.no_link_relax;
|
||||
}
|
||||
@ -855,7 +1023,8 @@ static unsigned int
|
||||
avr_operand (struct avr_opcodes_s *opcode,
|
||||
int where,
|
||||
const char *op,
|
||||
char **line)
|
||||
char **line,
|
||||
int *pregno)
|
||||
{
|
||||
expressionS op_expr;
|
||||
unsigned int op_mask = 0;
|
||||
@ -904,6 +1073,9 @@ avr_operand (struct avr_opcodes_s *opcode,
|
||||
}
|
||||
}
|
||||
|
||||
if (pregno)
|
||||
*pregno = op_mask;
|
||||
|
||||
if (avr_mcu->mach == bfd_mach_avrtiny)
|
||||
{
|
||||
if (op_mask < 16 || op_mask > 31)
|
||||
@ -1079,6 +1251,16 @@ avr_operand (struct avr_opcodes_s *opcode,
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
{
|
||||
unsigned int x;
|
||||
|
||||
x = avr_get_constant (str, 255);
|
||||
str = input_line_pointer;
|
||||
op_mask = x;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
input_line_pointer = str;
|
||||
avr_offset_expression (& op_expr);
|
||||
@ -1145,6 +1327,8 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
|
||||
char *str = *line;
|
||||
int where = frag - frag_now->fr_literal;
|
||||
static unsigned int prev = 0; /* Previous opcode. */
|
||||
int regno1 = -2;
|
||||
int regno2 = -2;
|
||||
|
||||
/* Opcode have operands. */
|
||||
if (*op)
|
||||
@ -1157,7 +1341,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
|
||||
/* Parse first operand. */
|
||||
if (REGISTER_P (*op))
|
||||
reg1_present = 1;
|
||||
reg1 = avr_operand (opcode, where, op, &str);
|
||||
reg1 = avr_operand (opcode, where, op, &str, ®no1);
|
||||
++op;
|
||||
|
||||
/* Parse second operand. */
|
||||
@ -1170,6 +1354,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
|
||||
{
|
||||
reg2 = reg1;
|
||||
reg2_present = 1;
|
||||
regno2 = regno1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1181,7 +1366,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
|
||||
as_bad (_("`,' required"));
|
||||
str = skip_space (str);
|
||||
|
||||
reg2 = avr_operand (opcode, where, op, &str);
|
||||
reg2 = avr_operand (opcode, where, op, &str, ®no2);
|
||||
}
|
||||
|
||||
if (reg1_present && reg2_present)
|
||||
@ -1194,6 +1379,9 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
|
||||
bin |= reg1 | reg2;
|
||||
}
|
||||
|
||||
if (avr_opt.have_gccisr)
|
||||
avr_update_gccisr (opcode, regno1, regno2);
|
||||
|
||||
/* Detect undefined combinations (like ld r31,Z+). */
|
||||
if (!avr_opt.all_opcodes && AVR_UNDEF_P (bin))
|
||||
as_warn (_("undefined combination of operands"));
|
||||
@ -1698,6 +1886,13 @@ md_assemble (char *str)
|
||||
return;
|
||||
}
|
||||
|
||||
if (opcode == avr_gccisr_opcode
|
||||
&& !avr_opt.have_gccisr)
|
||||
{
|
||||
as_bad (_("pseudo instruction `%s' not supported"), op);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Special case for opcodes with optional operands (lpm, elpm) -
|
||||
version with operands exists in avr_opcodes[] in the next entry. */
|
||||
|
||||
@ -1711,7 +1906,10 @@ md_assemble (char *str)
|
||||
{
|
||||
char *t = input_line_pointer;
|
||||
|
||||
avr_operands (opcode, &str);
|
||||
if (opcode == avr_gccisr_opcode)
|
||||
avr_gccisr_operands (opcode, &str);
|
||||
else
|
||||
avr_operands (opcode, &str);
|
||||
if (*skip_space (str))
|
||||
as_bad (_("garbage at end of line"));
|
||||
input_line_pointer = t;
|
||||
@ -2211,3 +2409,385 @@ avr_post_relax_hook (void)
|
||||
{
|
||||
avr_create_and_fill_property_section ();
|
||||
}
|
||||
|
||||
|
||||
/* Accumulate information about instruction sequence to `avr_isr':
|
||||
wheter TMP_REG, ZERO_REG and SREG might be touched. Used during parse.
|
||||
REG1 is either -1 or a register number used by the instruction as input
|
||||
or output operand. Similar for REG2. */
|
||||
|
||||
static void
|
||||
avr_update_gccisr (struct avr_opcodes_s *opcode, int reg1, int reg2)
|
||||
{
|
||||
const int tiny_p = avr_mcu->mach == bfd_mach_avrtiny;
|
||||
const int reg_tmp = tiny_p ? 16 : 0;
|
||||
const int reg_zero = 1 + reg_tmp;
|
||||
|
||||
if (ISR_CHUNK_Done == avr_isr.prev_chunk
|
||||
|| (avr_isr.need_sreg
|
||||
&& avr_isr.need_reg_tmp
|
||||
&& avr_isr.need_reg_zero))
|
||||
{
|
||||
/* Nothing (more) to do */
|
||||
return;
|
||||
}
|
||||
|
||||
/* SREG: Look up instructions that don't clobber SREG. */
|
||||
|
||||
if (!avr_isr.need_sreg
|
||||
&& !hash_find (avr_no_sreg_hash, opcode->name))
|
||||
{
|
||||
avr_isr.need_sreg = 1;
|
||||
}
|
||||
|
||||
/* Handle explicit register operands. Record *any* use as clobber.
|
||||
This is because TMP_REG and ZERO_REG are not global and using
|
||||
them makes no sense without a previous set. */
|
||||
|
||||
avr_isr.need_reg_tmp |= reg1 == reg_tmp || reg2 == reg_tmp;
|
||||
avr_isr.need_reg_zero |= reg1 == reg_zero || reg2 == reg_zero;
|
||||
|
||||
/* Handle implicit register operands and some opaque stuff. */
|
||||
|
||||
if (strstr (opcode->name, "lpm")
|
||||
&& '?' == *opcode->constraints)
|
||||
{
|
||||
avr_isr.need_reg_tmp = 1;
|
||||
}
|
||||
|
||||
if (strstr (opcode->name, "call")
|
||||
|| strstr (opcode->name, "mul")
|
||||
|| 0 == strcmp (opcode->name, "des")
|
||||
|| (0 == strcmp (opcode->name, "movw")
|
||||
&& (reg1 == reg_tmp || reg2 == reg_tmp)))
|
||||
{
|
||||
avr_isr.need_reg_tmp = 1;
|
||||
avr_isr.need_reg_zero = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Emit some 1-word instruction to **PWHERE and advance *PWHERE by the number
|
||||
of octets written. INSN specifies the desired instruction and REG is the
|
||||
register used by it. This function is only used with restricted subset of
|
||||
instructions as might be emit by `__gcc_isr'. IN / OUT will use SREG
|
||||
and LDI loads 0. */
|
||||
|
||||
static void
|
||||
avr_emit_insn (const char *insn, int reg, char **pwhere)
|
||||
{
|
||||
const int sreg = 0x3f;
|
||||
unsigned bin = 0;
|
||||
const struct avr_opcodes_s *op
|
||||
= (struct avr_opcodes_s*) hash_find (avr_hash, insn);
|
||||
|
||||
/* We only have to deal with: IN, OUT, PUSH, POP, CLR, LDI 0. All of
|
||||
these deal with at least one Reg and are 1-word instructions. */
|
||||
|
||||
gas_assert (op && 1 == op->insn_size);
|
||||
gas_assert (reg >= 0 && reg <= 31);
|
||||
|
||||
if (strchr (op->constraints, 'r'))
|
||||
{
|
||||
bin = op->bin_opcode | (reg << 4);
|
||||
}
|
||||
else if (strchr (op->constraints, 'd'))
|
||||
{
|
||||
gas_assert (reg >= 16);
|
||||
bin = op->bin_opcode | ((reg & 0xf) << 4);
|
||||
}
|
||||
else
|
||||
abort();
|
||||
|
||||
if (strchr (op->constraints, 'P'))
|
||||
{
|
||||
bin |= ((sreg & 0x30) << 5) | (sreg & 0x0f);
|
||||
}
|
||||
else if (0 == strcmp ("r=r", op->constraints))
|
||||
{
|
||||
bin |= ((reg & 0x10) << 5) | (reg & 0x0f);
|
||||
}
|
||||
else
|
||||
gas_assert (0 == strcmp ("r", op->constraints)
|
||||
|| 0 == strcmp ("ldi", op->name));
|
||||
|
||||
bfd_putl16 ((bfd_vma) bin, *pwhere);
|
||||
(*pwhere) += 2 * op->insn_size;
|
||||
}
|
||||
|
||||
|
||||
/* Turn rs_machine_dependent frag *FR into an ordinary rs_fill code frag,
|
||||
using information gathered in `avr_isr'. REG is the register number as
|
||||
supplied by Done chunk "__gcc_isr 0,REG". */
|
||||
|
||||
static void
|
||||
avr_patch_gccisr_frag (fragS *fr, int reg)
|
||||
{
|
||||
int treg;
|
||||
int n_pushed = 0;
|
||||
char *where = fr->fr_literal;
|
||||
const int tiny_p = avr_mcu->mach == bfd_mach_avrtiny;
|
||||
const int reg_tmp = tiny_p ? 16 : 0;
|
||||
const int reg_zero = 1 + reg_tmp;
|
||||
|
||||
/* Clearing ZERO_REG on non-Tiny needs CLR which clobbers SREG. */
|
||||
|
||||
avr_isr.need_sreg |= !tiny_p && avr_isr.need_reg_zero;
|
||||
|
||||
/* A working register to PUSH / POP the SREG. We might use the register
|
||||
as supplied by ISR_CHUNK_Done for that purpose as GCC wants to push
|
||||
it anyways. If GCC passes ZERO_REG or TMP_REG, it has no clue (and
|
||||
no additional regs to safe) and we use that reg. */
|
||||
|
||||
treg
|
||||
= avr_isr.need_reg_tmp ? reg_tmp
|
||||
: avr_isr.need_reg_zero ? reg_zero
|
||||
: avr_isr.need_sreg ? reg
|
||||
: reg > reg_zero ? reg
|
||||
: -1;
|
||||
|
||||
if (treg >= 0)
|
||||
{
|
||||
/* Non-empty prologue / epilogue */
|
||||
|
||||
if (ISR_CHUNK_Prologue == fr->fr_subtype)
|
||||
{
|
||||
avr_emit_insn ("push", treg, &where);
|
||||
n_pushed++;
|
||||
|
||||
if (avr_isr.need_sreg)
|
||||
{
|
||||
avr_emit_insn ("in", treg, &where);
|
||||
avr_emit_insn ("push", treg, &where);
|
||||
n_pushed++;
|
||||
}
|
||||
|
||||
if (avr_isr.need_reg_zero)
|
||||
{
|
||||
if (reg_zero != treg)
|
||||
{
|
||||
avr_emit_insn ("push", reg_zero, &where);
|
||||
n_pushed++;
|
||||
}
|
||||
avr_emit_insn (tiny_p ? "ldi" : "clr", reg_zero, &where);
|
||||
}
|
||||
|
||||
if (reg > reg_zero && reg != treg)
|
||||
{
|
||||
avr_emit_insn ("push", reg, &where);
|
||||
n_pushed++;
|
||||
}
|
||||
}
|
||||
else if (ISR_CHUNK_Epilogue == fr->fr_subtype)
|
||||
{
|
||||
/* Same logic as in Prologue but in reverse order and with counter
|
||||
parts of either instruction: POP instead of PUSH and OUT instead
|
||||
of IN. Clearing ZERO_REG has no couter part. */
|
||||
|
||||
if (reg > reg_zero && reg != treg)
|
||||
avr_emit_insn ("pop", reg, &where);
|
||||
|
||||
if (avr_isr.need_reg_zero
|
||||
&& reg_zero != treg)
|
||||
avr_emit_insn ("pop", reg_zero, &where);
|
||||
|
||||
if (avr_isr.need_sreg)
|
||||
{
|
||||
avr_emit_insn ("pop", treg, &where);
|
||||
avr_emit_insn ("out", treg, &where);
|
||||
}
|
||||
|
||||
avr_emit_insn ("pop", treg, &where);
|
||||
}
|
||||
else
|
||||
abort();
|
||||
} /* treg >= 0 */
|
||||
|
||||
if (ISR_CHUNK_Prologue == fr->fr_subtype
|
||||
&& avr_isr.sym_n_pushed)
|
||||
{
|
||||
symbolS *sy = avr_isr.sym_n_pushed;
|
||||
/* Turn magic `__gcc_isr.n_pushed' into its now known value. */
|
||||
|
||||
sy->sy_value.X_op = O_constant;
|
||||
sy->sy_value.X_add_number = n_pushed;
|
||||
S_SET_SEGMENT (sy, expr_section);
|
||||
avr_isr.sym_n_pushed = NULL;
|
||||
}
|
||||
|
||||
/* Turn frag into ordinary code frag of now known size. */
|
||||
|
||||
fr->fr_var = 0;
|
||||
fr->fr_fix = (offsetT) (where - fr->fr_literal);
|
||||
gas_assert (fr->fr_fix <= fr->fr_offset);
|
||||
fr->fr_offset = 0;
|
||||
fr->fr_type = rs_fill;
|
||||
fr->fr_subtype = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Implements `__gcc_isr' pseudo-instruction. For Prologue and Epilogue
|
||||
chunks, emit a new rs_machine_dependent frag. For Done chunks, traverse
|
||||
the current segment and patch all rs_machine_dependent frags to become
|
||||
appropriate rs_fill code frags. If chunks are seen in an odd ordering,
|
||||
throw an error instead. */
|
||||
|
||||
static void
|
||||
avr_gccisr_operands (struct avr_opcodes_s *opcode, char **line)
|
||||
{
|
||||
int bad = 0;
|
||||
int chunk, reg = 0;
|
||||
char *str = *line;
|
||||
|
||||
gas_assert (avr_opt.have_gccisr);
|
||||
|
||||
/* We only use operands "N" and "r" which don't pop new fix-ups. */
|
||||
|
||||
/* 1st operand: Which chunk of __gcc_isr: 0...2. */
|
||||
|
||||
chunk = avr_operand (opcode, -1, "N", &str, NULL);
|
||||
if (chunk < 0 || chunk > 2)
|
||||
as_bad (_("%s requires value 0-2 as operand 1"), opcode->name);
|
||||
|
||||
if (ISR_CHUNK_Done == chunk)
|
||||
{
|
||||
/* 2nd operand: A register to push / pop. */
|
||||
|
||||
str = skip_space (str);
|
||||
if (*str == '\0' || *str++ != ',')
|
||||
as_bad (_("`,' required"));
|
||||
else
|
||||
avr_operand (opcode, -1, "r", &str, ®);
|
||||
}
|
||||
|
||||
*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);
|
||||
}
|
||||
|
@ -220,6 +220,12 @@ extern bfd_boolean avr_allow_local_subtract (expressionS *, expressionS *, segT)
|
||||
#define elf_tc_final_processing avr_elf_final_processing
|
||||
extern void avr_elf_final_processing (void);
|
||||
|
||||
#define md_pre_output_hook avr_pre_output_hook ()
|
||||
extern void avr_pre_output_hook (void);
|
||||
|
||||
#define md_undefined_symbol avr_undefined_symbol
|
||||
extern symbolS* avr_undefined_symbol (char*);
|
||||
|
||||
#define md_post_relax_hook avr_post_relax_hook ()
|
||||
extern void avr_post_relax_hook (void);
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
* AVR Options:: Options
|
||||
* AVR Syntax:: Syntax
|
||||
* AVR Opcodes:: Opcodes
|
||||
* AVR Pseudo Instructions:: Pseudo Instructions
|
||||
@end menu
|
||||
|
||||
@node AVR Options
|
||||
@ -151,6 +152,10 @@ Disable support for link-time relaxation. The assembler will resolve
|
||||
relocations when it can, and may be able to better compress some debug
|
||||
information.
|
||||
|
||||
@cindex @code{-mgcc-isr} command line option, AVR
|
||||
@item -mgcc-isr
|
||||
Enable the @code{__gcc_isr} pseudo instruction.
|
||||
|
||||
@end table
|
||||
|
||||
|
||||
@ -441,3 +446,60 @@ The following table summarizes the AVR opcodes, and their arguments.
|
||||
1001010100011001 eicall
|
||||
1001010000011001 eijmp
|
||||
@end smallexample
|
||||
|
||||
@node AVR Pseudo Instructions
|
||||
@section Pseudo Instructions
|
||||
|
||||
The only available pseudo-instruction @code{__gcc_isr} can be activated by
|
||||
option @option{-mgcc-isr}.
|
||||
|
||||
@table @code
|
||||
|
||||
@item __gcc_isr 1
|
||||
Emit code chunk to be used in avr-gcc ISR prologue.
|
||||
It will expand to at most six 1-word instructions, all optional:
|
||||
push of @code{tmp_reg}, push of @code{SREG},
|
||||
push and clear of @code{zero_reg}, push of @var{Reg}.
|
||||
|
||||
@item __gcc_isr 2
|
||||
Emit code chunk to be used in an avr-gcc ISR epilogue.
|
||||
It will expand to at most five 1-word instructions, all optional:
|
||||
pop of @var{Reg}, pop of @code{zero_reg},
|
||||
pop of @code{SREG}, pop of @code{tmp_reg}.
|
||||
|
||||
@item __gcc_isr 0, @var{Reg}
|
||||
Finish avr-gcc ISR function. Scan code since the last prologue
|
||||
for usage of: @code{SREG}, @code{tmp_reg}, @code{zero_reg}.
|
||||
Prologue chunk and epilogue chunks will be replaced by appropriate code
|
||||
to save / restore @code{SREG}, @code{tmp_reg}, @code{zero_reg} and @var{Reg}.
|
||||
|
||||
@end table
|
||||
|
||||
Example input:
|
||||
|
||||
@example
|
||||
__vector1:
|
||||
__gcc_isr 1
|
||||
lds r24, var
|
||||
inc r24
|
||||
sts var, r24
|
||||
__gcc_isr 2
|
||||
reti
|
||||
__gcc_isr 0, r24
|
||||
@end example
|
||||
|
||||
Example output:
|
||||
|
||||
@example
|
||||
00000000 <__vector1>:
|
||||
0: 8f 93 push r24
|
||||
2: 8f b7 in r24, 0x3f
|
||||
4: 8f 93 push r24
|
||||
6: 80 91 60 00 lds r24, 0x0060 ; 0x800060 <var>
|
||||
a: 83 95 inc r24
|
||||
c: 80 93 60 00 sts 0x0060, r24 ; 0x800060 <var>
|
||||
10: 8f 91 pop r24
|
||||
12: 8f bf out 0x3f, r24
|
||||
14: 8f 91 pop r24
|
||||
16: 18 95 reti
|
||||
@end example
|
||||
|
141
gas/testsuite/gas/avr/gccisr-01.d
Normal file
141
gas/testsuite/gas/avr/gccisr-01.d
Normal file
@ -0,0 +1,141 @@
|
||||
#name: gccisr-01: __gcc_isr pseudo instruction
|
||||
#as: -mgcc-isr -mavr4
|
||||
#objdump: -dz
|
||||
#target: avr-*-*
|
||||
|
||||
.*: +file format elf32-avr
|
||||
|
||||
|
||||
Disassembly of section \.text:
|
||||
|
||||
00000000 <__start1>:
|
||||
0: 68 94 set
|
||||
|
||||
00000002 <__vec1_start>:
|
||||
2: 0f 92 push r0
|
||||
4: 0f b6 in r0, 0x3f ; 63
|
||||
6: 0f 92 push r0
|
||||
8: 01 30 cpi r16, 0x01 ; 1
|
||||
a: 0f 90 pop r0
|
||||
c: 0f be out 0x3f, r0 ; 63
|
||||
e: 0f 90 pop r0
|
||||
10: e8 94 clt
|
||||
|
||||
00000012 <__data1>:
|
||||
12: 00 e0 ldi r16, 0x00 ; 0
|
||||
14: 08 00 \.word 0x0008 ; \?\?\?\?
|
||||
|
||||
00000016 <__start2>:
|
||||
16: 68 94 set
|
||||
|
||||
00000018 <__vec2_start>:
|
||||
18: e1 e0 ldi r30, 0x01 ; 1
|
||||
1a: f0 91 00 00 lds r31, 0x0000 ; 0x800000 <__data6\+0x7fff40>
|
||||
1e: f0 93 00 00 sts 0x0000, r31 ; 0x800000 <__data6\+0x7fff40>
|
||||
22: 12 01 movw r2, r4
|
||||
24: 12 95 swap r17
|
||||
26: 18 95 reti
|
||||
28: 78 10 cpse r7, r8
|
||||
2a: 78 94 sei
|
||||
2c: f8 94 cli
|
||||
2e: af b6 in r10, 0x3f ; 63
|
||||
30: af be out 0x3f, r10 ; 63
|
||||
32: 18 95 reti
|
||||
34: e8 94 clt
|
||||
|
||||
00000036 <__data2>:
|
||||
36: 00 e0 ldi r16, 0x00 ; 0
|
||||
38: 0f 00 \.word 0x000f ; \?\?\?\?
|
||||
|
||||
0000003a <__start3>:
|
||||
3a: 68 94 set
|
||||
|
||||
0000003c <__vec3_start>:
|
||||
3c: 1f 92 push r1
|
||||
3e: 1f b6 in r1, 0x3f ; 63
|
||||
40: 1f 92 push r1
|
||||
42: 11 24 eor r1, r1
|
||||
44: 8f 93 push r24
|
||||
46: 8f 91 pop r24
|
||||
48: 1f 90 pop r1
|
||||
4a: 1f be out 0x3f, r1 ; 63
|
||||
4c: 1f 90 pop r1
|
||||
4e: 18 95 reti
|
||||
50: 8f 91 pop r24
|
||||
52: 1f 90 pop r1
|
||||
54: 1f be out 0x3f, r1 ; 63
|
||||
56: 1f 90 pop r1
|
||||
58: 18 95 reti
|
||||
5a: 13 94 inc r1
|
||||
5c: e8 94 clt
|
||||
|
||||
0000005e <__data3>:
|
||||
5e: 00 e0 ldi r16, 0x00 ; 0
|
||||
60: 11 00 \.word 0x0011 ; \?\?\?\?
|
||||
|
||||
00000062 <__start4>:
|
||||
62: 68 94 set
|
||||
|
||||
00000064 <__vec4_start>:
|
||||
64: 0f 92 push r0
|
||||
66: 0f b6 in r0, 0x3f ; 63
|
||||
68: 0f 92 push r0
|
||||
6a: 1f 92 push r1
|
||||
6c: 11 24 eor r1, r1
|
||||
6e: 8f 93 push r24
|
||||
70: 8f 91 pop r24
|
||||
72: 1f 90 pop r1
|
||||
74: 0f 90 pop r0
|
||||
76: 0f be out 0x3f, r0 ; 63
|
||||
78: 0f 90 pop r0
|
||||
7a: 18 95 reti
|
||||
7c: 8f 91 pop r24
|
||||
7e: 1f 90 pop r1
|
||||
80: 0f 90 pop r0
|
||||
82: 0f be out 0x3f, r0 ; 63
|
||||
84: 0f 90 pop r0
|
||||
86: 18 95 reti
|
||||
88: 01 9f mul r16, r17
|
||||
8a: e8 94 clt
|
||||
|
||||
0000008c <__data4>:
|
||||
8c: 00 e0 ldi r16, 0x00 ; 0
|
||||
8e: 14 00 \.word 0x0014 ; \?\?\?\?
|
||||
|
||||
00000090 <__start5>:
|
||||
90: 68 94 set
|
||||
|
||||
00000092 <__vec5_start>:
|
||||
92: 0f 92 push r0
|
||||
94: c8 95 lpm
|
||||
96: 0f 90 pop r0
|
||||
98: 18 95 reti
|
||||
9a: 0f 90 pop r0
|
||||
9c: 18 95 reti
|
||||
9e: e8 94 clt
|
||||
|
||||
000000a0 <__data5>:
|
||||
a0: 00 e0 ldi r16, 0x00 ; 0
|
||||
a2: 07 00 \.word 0x0007 ; \?\?\?\?
|
||||
|
||||
000000a4 <__start6>:
|
||||
a4: 68 94 set
|
||||
|
||||
000000a6 <__vec6_start>:
|
||||
a6: af 93 push r26
|
||||
a8: af b7 in r26, 0x3f ; 63
|
||||
aa: af 93 push r26
|
||||
ac: af 91 pop r26
|
||||
ae: af bf out 0x3f, r26 ; 63
|
||||
b0: af 91 pop r26
|
||||
b2: 18 95 reti
|
||||
b4: af 91 pop r26
|
||||
b6: af bf out 0x3f, r26 ; 63
|
||||
b8: af 91 pop r26
|
||||
ba: 18 95 reti
|
||||
bc: 88 94 clc
|
||||
be: e8 94 clt
|
||||
|
||||
000000c0 <__data6>:
|
||||
c0: 00 e0 ldi r16, 0x00 ; 0
|
||||
c2: 0d 00 \.word 0x000d ; \?\?\?\?
|
127
gas/testsuite/gas/avr/gccisr-01.s
Normal file
127
gas/testsuite/gas/avr/gccisr-01.s
Normal file
@ -0,0 +1,127 @@
|
||||
.text
|
||||
|
||||
;;; Use SREG
|
||||
|
||||
__start1:
|
||||
set
|
||||
|
||||
__vec1_start:
|
||||
__gcc_isr 1
|
||||
foo = __gcc_isr.n_pushed
|
||||
cpi r16,1
|
||||
__gcc_isr 2
|
||||
__gcc_isr 0,r0
|
||||
clt
|
||||
__vec1_end:
|
||||
__data1:
|
||||
ldi r16, foo - 2
|
||||
.word (__vec1_end - __vec1_start) / 2
|
||||
|
||||
;;; Nothing used.
|
||||
|
||||
__start2:
|
||||
set
|
||||
|
||||
__vec2_start:
|
||||
__gcc_isr 1
|
||||
foo = __gcc_isr.n_pushed
|
||||
ldi r30, 1
|
||||
lds r31, 0
|
||||
sts 0, r31
|
||||
movw r2, r4
|
||||
swap r17
|
||||
__gcc_isr 2
|
||||
reti
|
||||
__gcc_isr 2
|
||||
cpse r7, r8
|
||||
sei
|
||||
cli
|
||||
in r10, 0x3f
|
||||
out 0x3f, r10
|
||||
reti
|
||||
__gcc_isr 0,r0
|
||||
clt
|
||||
__vec2_end:
|
||||
__data2:
|
||||
ldi r16, foo - 0
|
||||
.word (__vec2_end - __vec2_start) / 2
|
||||
|
||||
;;; Use SREG, ZERO and R24
|
||||
|
||||
__start3:
|
||||
set
|
||||
|
||||
__vec3_start:
|
||||
__gcc_isr 1
|
||||
foo = __gcc_isr.n_pushed
|
||||
__gcc_isr 2
|
||||
reti
|
||||
__gcc_isr 2
|
||||
reti
|
||||
inc r1
|
||||
__gcc_isr 0,r24
|
||||
clt
|
||||
__vec3_end:
|
||||
__data3:
|
||||
ldi r16, foo - 3
|
||||
.word (__vec3_end - __vec3_start) / 2
|
||||
|
||||
;;; Use SREG, ZERO, TMP and R24
|
||||
|
||||
__start4:
|
||||
set
|
||||
|
||||
__vec4_start:
|
||||
__gcc_isr 1
|
||||
foo = __gcc_isr.n_pushed
|
||||
__gcc_isr 2
|
||||
reti
|
||||
__gcc_isr 2
|
||||
reti
|
||||
mul 16, 17
|
||||
__gcc_isr 0,r24
|
||||
clt
|
||||
__vec4_end:
|
||||
__data4:
|
||||
ldi r16, foo - 4
|
||||
.word (__vec4_end - __vec4_start) / 2
|
||||
|
||||
;;; Use TMP
|
||||
|
||||
__start5:
|
||||
set
|
||||
|
||||
__vec5_start:
|
||||
__gcc_isr 1
|
||||
lpm
|
||||
foo = __gcc_isr.n_pushed
|
||||
__gcc_isr 2
|
||||
reti
|
||||
__gcc_isr 2
|
||||
reti
|
||||
__gcc_isr 0,r0
|
||||
clt
|
||||
__vec5_end:
|
||||
__data5:
|
||||
ldi r16, foo - 1
|
||||
.word (__vec5_end - __vec5_start) / 2
|
||||
|
||||
;;; Use SREG, R26
|
||||
|
||||
__start6:
|
||||
set
|
||||
|
||||
__vec6_start:
|
||||
__gcc_isr 1
|
||||
foo = __gcc_isr.n_pushed
|
||||
__gcc_isr 2
|
||||
reti
|
||||
__gcc_isr 2
|
||||
reti
|
||||
clc
|
||||
__gcc_isr 0,r26
|
||||
clt
|
||||
__vec6_end:
|
||||
__data6:
|
||||
ldi r16, foo - 2
|
||||
.word (__vec6_end - __vec6_start) / 2
|
43
gas/testsuite/gas/avr/gccisr-02.d
Normal file
43
gas/testsuite/gas/avr/gccisr-02.d
Normal file
@ -0,0 +1,43 @@
|
||||
#name: gccisr-02: __gcc_isr pseudo instruction
|
||||
#as: -mgcc-isr -mavrtiny
|
||||
#objdump: -dz
|
||||
#target: avr-*-*
|
||||
|
||||
.*: +file format elf32-avr
|
||||
|
||||
|
||||
Disassembly of section \.text:
|
||||
|
||||
00000000 <__start1>:
|
||||
0: 68 94 set
|
||||
|
||||
00000002 <__vec1_start>:
|
||||
2: 0f 93 push r16
|
||||
4: 0f b7 in r16, 0x3f ; 63
|
||||
6: 0f 93 push r16
|
||||
8: 21 30 cpi r18, 0x01 ; 1
|
||||
a: 0f 91 pop r16
|
||||
c: 0f bf out 0x3f, r16 ; 63
|
||||
e: 0f 91 pop r16
|
||||
10: e8 94 clt
|
||||
|
||||
00000012 <__data1>:
|
||||
12: 00 e0 ldi r16, 0x00 ; 0
|
||||
14: 08 00 \.word 0x0008 ; \?\?\?\?
|
||||
|
||||
00000016 <__start2>:
|
||||
16: 68 94 set
|
||||
|
||||
00000018 <__vec2_start>:
|
||||
18: 1f 93 push r17
|
||||
1a: 10 e0 ldi r17, 0x00 ; 0
|
||||
1c: 1f 91 pop r17
|
||||
1e: 18 95 reti
|
||||
20: e1 2f mov r30, r17
|
||||
22: 1f 91 pop r17
|
||||
24: 18 95 reti
|
||||
26: e8 94 clt
|
||||
|
||||
00000028 <__data2>:
|
||||
28: 00 e0 ldi r16, 0x00 ; 0
|
||||
2a: 08 00 \.word 0x0008 ; \?\?\?\?
|
38
gas/testsuite/gas/avr/gccisr-02.s
Normal file
38
gas/testsuite/gas/avr/gccisr-02.s
Normal file
@ -0,0 +1,38 @@
|
||||
.text
|
||||
|
||||
;;; Use SREG
|
||||
|
||||
__start1:
|
||||
set
|
||||
|
||||
__vec1_start:
|
||||
__gcc_isr 1
|
||||
foo = __gcc_isr.n_pushed
|
||||
cpi r18,1
|
||||
__gcc_isr 2
|
||||
__gcc_isr 0,r16
|
||||
clt
|
||||
__vec1_end:
|
||||
__data1:
|
||||
ldi r16, foo - 2
|
||||
.word (__vec1_end - __vec1_start) / 2
|
||||
|
||||
;;; Use ZERO
|
||||
|
||||
__start2:
|
||||
set
|
||||
|
||||
__vec2_start:
|
||||
__gcc_isr 1
|
||||
foo = __gcc_isr.n_pushed
|
||||
__gcc_isr 2
|
||||
reti
|
||||
mov r30,r17
|
||||
__gcc_isr 2
|
||||
reti
|
||||
__gcc_isr 0,r16
|
||||
clt
|
||||
__vec2_end:
|
||||
__data2:
|
||||
ldi r16, foo - 1
|
||||
.word (__vec2_end - __vec2_start) / 2
|
4
gas/testsuite/gas/avr/gccisr-03.d
Normal file
4
gas/testsuite/gas/avr/gccisr-03.d
Normal file
@ -0,0 +1,4 @@
|
||||
#name: __gcc_isr pseudo instruction, test gccisr-03
|
||||
#as:
|
||||
#error: pseudo instruction `__gcc_isr' not supported
|
||||
#target: avr-*-*
|
6
gas/testsuite/gas/avr/gccisr-03.s
Normal file
6
gas/testsuite/gas/avr/gccisr-03.s
Normal file
@ -0,0 +1,6 @@
|
||||
.text
|
||||
|
||||
;;;
|
||||
|
||||
__start1:
|
||||
__gcc_isr 1
|
@ -1,3 +1,8 @@
|
||||
2017-06-30 Georg-Johann Lay <avr@gjlay.de>
|
||||
|
||||
PR gas/21683
|
||||
* opcode/avr.h (AVR_INSN): Add one for __gcc_isr.
|
||||
|
||||
2017-06-30 Maciej W. Rozycki <macro@imgtec.com>
|
||||
Andrew Bennett <andrew.bennett@imgtec.com>
|
||||
|
||||
|
@ -110,6 +110,7 @@
|
||||
z - Z pointer register (for [e]lpm Rd,Z[+])
|
||||
M - immediate value from 0 to 255
|
||||
n - immediate value from 0 to 255 ( n = ~M ). Relocation impossible
|
||||
N - immediate value from 0 to 255. Relocation impossible
|
||||
s - immediate value from 0 to 7
|
||||
P - Port address value from 0 to 63. (in, out)
|
||||
p - Port address value from 0 to 31. (cbi, sbi, sbic, sbis)
|
||||
@ -306,3 +307,7 @@ AVR_INSN (eijmp, "", "1001010000011001", 1, AVR_ISA_EIND, 0x9419)
|
||||
/* DES instruction for encryption and decryption. */
|
||||
AVR_INSN (des, "E", "10010100EEEE1011", 1, AVR_ISA_DES, 0x940B)
|
||||
|
||||
/* Operands are evaluated by hand and won't pop new fux-ups.
|
||||
The pseudo-insn is hidden behind NOP so that avr-dis.c don't see it. */
|
||||
AVR_INSN (__gcc_isr, "", "0000000000000000", 1, AVR_ISA_1200, 0x0)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user