This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [patch,gas,avr] PR21683: Add new pseudo instruction __gcc_isr


On 29.06.2017 14:43, Nick Clifton wrote:
Hi Georg-Johann,

   In general I think that the patch is OK, but there are a few small niggles
   that I would like to see fixed:

+	chunk 1 (Prologue) or chunk 2 (Epilogue).  Funktions might come
+	without epilogue or with more than one epilogue, and even code
+	located statically after the last epologue might belong to a function.

Typos: Funktions and epologue

Fixed.

+static struct
+{
+  /* Previous __gcc_isr chunk (one of the enums above or 0),
+     and it's location for diagnostics.  */
+  int prev_chunk;

Umm - 0 is a valid value for the enum you are using.  Either the
enum should not contain 0, or else a different value should be used
to indicate no-previous-chunk, or else the meaning of this field should
be changed.

Fixed.

There is no difference between "no-previous chunk" and "chunk Done",
that is what I tried to express.


+  if (ISR_CHUNK_Done != avr_isr.prev_chunk
+      && name[0] == '_' && name[1] == '_'
+      && 0 == strcmp (name, "__gcc_isr.n_pushed"))

Why do you bother checking name[0] and name[1] ?  Unless speed of assembly
is a real issue, you could just simplify the code and drop these two tests.

Fixed.

+    {
+      if (!avr_isr.sym_n_pushed)
+	{
+	  static int suffix;
+	  char xname[30];
+	  sprintf (xname, "%s.%u", name, ++suffix);

Paranoia - 30 bytes may not be enough for a pathological input file which
contains 2^31 nested symbol pushes...  Using snprintf would catch this problem.

now using

    sprintf (xname, "%s.%03u", name, (++suffix) % 1000);

The biggest AVRs don't have more than 127 vector entries, so
a module with such many ISR means the compiler went nuts...

The suffix is actually only seen in GAS listings as the symbols are
soon transformed into O_constant expressions.  These symbols might
even have all the same name actually.  Hence if the above is too much
obfuscation, using the original "name" would also do.

Johann



Cheers
   Nick

diff --git a/gas/config/tc-avr.c b/gas/config/tc-avr.c
index 79837c8..7e3f719 100644
--- a/gas/config/tc-avr.c
+++ b/gas/config/tc-avr.c
@@ -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, &regno1);
       ++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, &regno2);
 	    }
 
 	  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, &reg);
+    }
+
+  *line = str;
+
+  /* Chunks must follow in a specific order:
+     - Prologue: Exactly one
+     - Epilogue: Any number
+     - Done: Exactly one.  */
+  bad |= ISR_CHUNK_Prologue == chunk && avr_isr.prev_chunk != ISR_CHUNK_Done;
+  bad |= ISR_CHUNK_Epilogue == chunk && avr_isr.prev_chunk == ISR_CHUNK_Done;
+  bad |= ISR_CHUNK_Done == chunk && avr_isr.prev_chunk == ISR_CHUNK_Done;
+  if (bad)
+    {
+      if (avr_isr.file)
+	as_bad (_("`%s %d' after `%s %d' from %s:%u"), opcode->name, chunk,
+		opcode->name, avr_isr.prev_chunk, avr_isr.file, avr_isr.line);
+      else
+	as_bad (_("`%s %d' but no chunk open yet"), opcode->name, chunk);
+    }
+
+  if (!had_errors())
+    {
+      /* The longest sequence (prologue) might have up to 6 insns (words):
+
+	 push  R0
+	 in    R0, SREG
+	 push  R0
+	 push  R1
+	 clr   R1
+	 push  Rx
+      */
+      unsigned int size = 2 * 6;
+      fragS *fr;
+
+      switch (chunk)
+	{
+	case ISR_CHUNK_Prologue:
+	  avr_isr.need_reg_tmp = 0;
+	  avr_isr.need_reg_zero = 0;
+	  avr_isr.need_sreg = 0;
+	  avr_isr.sym_n_pushed = NULL;
+	  /* FALLTHRU */
+
+	case ISR_CHUNK_Epilogue:
+	  /* Emit a new rs_machine_dependent fragment into the fragment chain.
+	     It will be patched and cleaned up once we see the matching
+	     ISR_CHUNK_Done.  */
+	  frag_wane (frag_now);
+	  frag_new (0);
+	  frag_more (size);
+
+	  frag_now->fr_var = 1;
+	  frag_now->fr_offset = size;
+	  frag_now->fr_fix = 0;
+	  frag_now->fr_type = rs_machine_dependent;
+	  frag_now->fr_subtype = chunk;
+	  frag_new (size);
+	  break;
+
+	case ISR_CHUNK_Done:
+	  /* Traverse all frags of the current subseg and turn ones of type
+	     rs_machine_dependent into ordinary code as expected by GCC.  */
+
+	  for (fr = frchain_now->frch_root; fr; fr = fr->fr_next)
+	    if (fr->fr_type == rs_machine_dependent)
+	      avr_patch_gccisr_frag (fr, reg);
+	  break;
+
+	default:
+	  abort();
+	  break;
+	}
+    } /* !had_errors */
+
+  avr_isr.prev_chunk = chunk;
+  avr_isr.file = as_where (&avr_isr.line);
+}
+
+
+/* Callback used by the function below.  Diagnose any dangling stuff from
+   `__gcc_isr', i.e. frags of type rs_machine_dependent.  Such frags should
+   have been resolved during parse by ISR_CHUNK_Done.  If such a frag is
+   seen, report an error and turn it into something harmless.  */
+
+static void
+avr_check_gccisr_done (bfd *abfd ATTRIBUTE_UNUSED,
+		       segT section,
+		       void *xxx ATTRIBUTE_UNUSED)
+{
+  segment_info_type *info = seg_info (section);
+
+  if (SEG_NORMAL (section)
+      /* BFD may have introduced its own sections without using
+	 subseg_new, so it is possible that seg_info is NULL.  */
+      && info)
+    {
+      fragS *fr;
+      frchainS *frch;
+
+      for (frch = info->frchainP; frch; frch = frch->frch_next)
+	for (fr = frch->frch_root; fr; fr = fr->fr_next)
+	  if (fr->fr_type == rs_machine_dependent)
+	    {
+	      if (avr_isr.file)
+		as_bad_where (avr_isr.file, avr_isr.line,
+			      _("dangling `__gcc_isr %d'"), avr_isr.prev_chunk);
+	      else if (!had_errors())
+		as_bad (_("dangling `__gcc_isr'"));
+
+	      avr_isr.file = NULL;
+
+	      /* Avoid Internal errors due to rs_machine_dependent in the
+		 remainder:  Turn frag into something harmless.   */
+	      fr->fr_var = 0;
+	      fr->fr_fix = 0;
+	      fr->fr_offset = 0;
+	      fr->fr_type = rs_fill;
+	      fr->fr_subtype = 0;
+	    }
+    }
+}
+
+
+/* Implement `md_pre_output_hook' */
+/* Run over all relevant sections and diagnose any dangling `__gcc_isr'.
+   This runs after parsing all inputs but before relaxing and writing.  */
+
+void
+avr_pre_output_hook (void)
+{
+  if (avr_opt.have_gccisr)
+    bfd_map_over_sections (stdoutput, avr_check_gccisr_done, NULL);
+}
diff --git a/gas/config/tc-avr.h b/gas/config/tc-avr.h
index 399656f..0cfe9ff 100644
--- a/gas/config/tc-avr.h
+++ b/gas/config/tc-avr.h
@@ -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);
 
diff --git a/gas/doc/c-avr.texi b/gas/doc/c-avr.texi
index f829e79..e419964 100644
--- a/gas/doc/c-avr.texi
+++ b/gas/doc/c-avr.texi
@@ -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
diff --git a/gas/testsuite/gas/avr/gccisr-01.d b/gas/testsuite/gas/avr/gccisr-01.d
new file mode 100644
index 0000000..91e1e61
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-01.d
@@ -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	; \?\?\?\?
diff --git a/gas/testsuite/gas/avr/gccisr-01.s b/gas/testsuite/gas/avr/gccisr-01.s
new file mode 100644
index 0000000..82cf9f6
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-01.s
@@ -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
diff --git a/gas/testsuite/gas/avr/gccisr-02.d b/gas/testsuite/gas/avr/gccisr-02.d
new file mode 100644
index 0000000..b0724c4
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-02.d
@@ -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	; \?\?\?\?
diff --git a/gas/testsuite/gas/avr/gccisr-02.s b/gas/testsuite/gas/avr/gccisr-02.s
new file mode 100644
index 0000000..167c42d1
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-02.s
@@ -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
diff --git a/gas/testsuite/gas/avr/gccisr-03.d b/gas/testsuite/gas/avr/gccisr-03.d
new file mode 100644
index 0000000..0eaa28d
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-03.d
@@ -0,0 +1,4 @@
+#name: __gcc_isr pseudo instruction, test gccisr-03
+#as: 
+#error: pseudo instruction `__gcc_isr' not supported
+#target: avr-*-*
diff --git a/gas/testsuite/gas/avr/gccisr-03.s b/gas/testsuite/gas/avr/gccisr-03.s
new file mode 100644
index 0000000..39938c8
--- /dev/null
+++ b/gas/testsuite/gas/avr/gccisr-03.s
@@ -0,0 +1,6 @@
+.text
+
+;;; 
+
+__start1:
+    __gcc_isr 1
diff --git a/include/opcode/avr.h b/include/opcode/avr.h
index 1c73022..2212816 100644
--- a/include/opcode/avr.h
+++ b/include/opcode/avr.h
@@ -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)
+

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]