]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/macro.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gas / macro.c
index f7d53bc673f36c0c5c4adc4aca547879a62d54a7..9aa2ebfc7a02eff6e9e24e73430657127800da48 100644 (file)
@@ -1,6 +1,5 @@
 /* macro.c - macro support for gas
-   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-   2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 1994-2021 Free Software Foundation, Inc.
 
    Written by Steve and Judy Chamberlain of Cygnus Support,
       sac@cygnus.com
@@ -9,7 +8,7 @@
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to the Free
-   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
-
-#include "config.h"
-
-#ifndef __GNUC__
-# if HAVE_ALLOCA_H
-#  include <alloca.h>
-# else
-#  ifdef _AIX
-/* Indented so that pre-ansi C compilers will ignore it, rather than
-   choke on it.  Some versions of AIX require this to be the first
-   thing in the file.  */
- #pragma alloca
-#  else
-#   ifndef alloca /* predefined by HP cc +Olibcalls */
-#    if !defined (__STDC__) && !defined (__hpux)
-extern char *alloca ();
-#    else
-extern void *alloca ();
-#    endif /* __STDC__, __hpux */
-#   endif /* alloca */
-#  endif /* _AIX */
-# endif /* HAVE_ALLOCA_H */
-#endif /* __GNUC__ */
-
-#include <stdio.h>
-#ifdef HAVE_STRING_H
-#include <string.h>
-#else
-#include <strings.h>
-#endif
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
 #include "as.h"
-#include "libiberty.h"
 #include "safe-ctype.h"
 #include "sb.h"
-#include "hash.h"
 #include "macro.h"
 
-#include "asintl.h"
-
 /* The routines in this file handle macro definition and expansion.
    They are called by gas.  */
 
-/* Internal functions.  */
-
-static int get_token (int, sb *, sb *);
-static int getstring (int, sb *, sb *);
-static int get_any_string (int, sb *, sb *, int, int);
-static int do_formals (macro_entry *, int, sb *);
-static int get_apost_token (int, sb *, sb *, int);
-static int sub_actual (int, sb *, sb *, struct hash_control *, int, sb *, int);
-static const char *macro_expand_body
-  (sb *, sb *, formal_entry *, struct hash_control *, const macro_entry *);
-static const char *macro_expand (int, sb *, macro_entry *, sb *);
-static void free_macro(macro_entry *);
-
 #define ISWHITE(x) ((x) == ' ' || (x) == '\t')
 
 #define ISSEP(x) \
@@ -94,7 +44,7 @@ static void free_macro(macro_entry *);
 
 /* The macro hash table.  */
 
-struct hash_control *macro_hash;
+struct htab *macro_hash;
 
 /* Whether any macros have been defined.  */
 
@@ -114,7 +64,7 @@ static int macro_strip_at;
 
 /* Function to use to parse an expression.  */
 
-static int (*macro_expr) (const char *, int, sb *, int *);
+static size_t (*macro_expr) (const char *, size_t, sb *, offsetT *);
 
 /* Number of macro expansions that have been done.  */
 
@@ -124,14 +74,15 @@ static int macro_number;
 
 void
 macro_init (int alternate, int mri, int strip_at,
-           int (*expr) (const char *, int, sb *, int *))
+           size_t (*exp) (const char *, size_t, sb *, offsetT *))
 {
-  macro_hash = hash_new ();
+  macro_hash = htab_create_alloc (16, hash_macro_entry, eq_macro_entry,
+                                 NULL, xcalloc, free);
   macro_defined = 0;
   macro_alternate = alternate;
   macro_mri = mri;
   macro_strip_at = strip_at;
-  macro_expr = expr;
+  macro_expr = exp;
 }
 
 /* Switch in and out of alternate mode on the fly.  */
@@ -159,16 +110,15 @@ macro_mri_mode (int mri)
 
 int
 buffer_and_nest (const char *from, const char *to, sb *ptr,
-                int (*get_line) (sb *))
+                size_t (*get_line) (sb *))
 {
-  int from_len;
-  int to_len = strlen (to);
+  size_t from_len;
+  size_t to_len = strlen (to);
   int depth = 1;
-  int line_start = ptr->len;
-
-  int more = get_line (ptr);
+  size_t line_start = ptr->len;
+  size_t more = get_line (ptr);
 
-  if (to_len == 4 && strcasecmp(to, "ENDR") == 0)
+  if (to_len == 4 && strcasecmp (to, "ENDR") == 0)
     {
       from = NULL;
       from_len = 0;
@@ -178,49 +128,52 @@ buffer_and_nest (const char *from, const char *to, sb *ptr,
 
   while (more)
     {
-      /* Try and find the first pseudo op on the line.  */
-      int i = line_start;
+      /* Try to find the first pseudo op on the line.  */
+      size_t i = line_start;
+      bfd_boolean had_colon = FALSE;
 
-      if (! NO_PSEUDO_DOT && ! flag_m68k_mri)
-       {
-         /* With normal syntax we can suck what we want till we get
-            to the dot.  With the alternate, labels have to start in
-            the first column, since we can't tell what's a label and
-            whats a pseudoop.  */
+      /* With normal syntax we can suck what we want till we get
+        to the dot.  With the alternate, labels have to start in
+        the first column, since we can't tell what's a label and
+        what's a pseudoop.  */
 
-         if (! LABELS_WITHOUT_COLONS)
-           {
-             /* Skip leading whitespace.  */
-             while (i < ptr->len && ISWHITE (ptr->ptr[i]))
-               i++;
-           }
+      if (! LABELS_WITHOUT_COLONS)
+       {
+         /* Skip leading whitespace.  */
+         while (i < ptr->len && ISWHITE (ptr->ptr[i]))
+           i++;
+       }
 
-         for (;;)
+      for (;;)
+       {
+         /* Skip over a label, if any.  */
+         if (i >= ptr->len || ! is_name_beginner (ptr->ptr[i]))
+           break;
+         i++;
+         while (i < ptr->len && is_part_of_name (ptr->ptr[i]))
+           i++;
+         if (i < ptr->len && is_name_ender (ptr->ptr[i]))
+           i++;
+         /* Skip whitespace.  */
+         while (i < ptr->len && ISWHITE (ptr->ptr[i]))
+           i++;
+         /* Check for the colon.  */
+         if (i >= ptr->len || ptr->ptr[i] != ':')
            {
-             /* Skip over a label, if any.  */
-             if (i >= ptr->len || ! is_name_beginner (ptr->ptr[i]))
-               break;
-             i++;
-             while (i < ptr->len && is_part_of_name (ptr->ptr[i]))
-               i++;
-             if (i < ptr->len && is_name_ender (ptr->ptr[i]))
-               i++;
-             if (LABELS_WITHOUT_COLONS)
+             /* LABELS_WITHOUT_COLONS doesn't mean we cannot have a
+                colon after a label.  If we do have a colon on the
+                first label then handle more than one label on the
+                line, assuming that each label has a colon.  */
+             if (LABELS_WITHOUT_COLONS && !had_colon)
                break;
-             /* Skip whitespace.  */
-             while (i < ptr->len && ISWHITE (ptr->ptr[i]))
-               i++;
-             /* Check for the colon.  */
-             if (i >= ptr->len || ptr->ptr[i] != ':')
-               {
-                 i = line_start;
-                 break;
-               }
-             i++;
-             line_start = i;
+             i = line_start;
+             break;
            }
-
+         i++;
+         line_start = i;
+         had_colon = TRUE;
        }
+
       /* Skip trailing whitespace.  */
       while (i < ptr->len && ISWHITE (ptr->ptr[i]))
        i++;
@@ -259,6 +212,27 @@ buffer_and_nest (const char *from, const char *to, sb *ptr,
                  break;
                }
            }
+
+         /* PR gas/16908
+            Apply and discard .linefile directives that appear within
+            the macro.  For long macros, one might want to report the
+            line number information associated with the lines within
+            the macro definition, but we would need more infrastructure
+            to make that happen correctly (e.g. resetting the line
+            number when expanding the macro), and since for short
+            macros we clearly prefer reporting the point of expansion
+            anyway, there's not an obviously better fix here.  */
+         if (strncasecmp (ptr->ptr + i, "linefile", 8) == 0)
+           {
+             char saved_eol_char = ptr->ptr[ptr->len];
+
+             ptr->ptr[ptr->len] = '\0';
+             temp_ilp (ptr->ptr + i + 8);
+             s_app_line (0);
+             restore_ilp ();
+             ptr->ptr[ptr->len] = saved_eol_char;
+             ptr->len = line_start;
+           }
        }
 
       /* Add the original end-of-line char to the end and keep running.  */
@@ -273,8 +247,8 @@ buffer_and_nest (const char *from, const char *to, sb *ptr,
 
 /* Pick up a token.  */
 
-static int
-get_token (int idx, sb *in, sb *name)
+static size_t
+get_token (size_t idx, sb *in, sb *name)
 {
   if (idx < in->len
       && is_name_beginner (in->ptr[idx]))
@@ -299,11 +273,9 @@ get_token (int idx, sb *in, sb *name)
 
 /* Pick up a string.  */
 
-static int
-getstring (int idx, sb *in, sb *acc)
+static size_t
+getstring (size_t idx, sb *in, sb *acc)
 {
-  idx = sb_skip_white (idx, in);
-
   while (idx < in->len
         && (in->ptr[idx] == '"'
             || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
@@ -313,8 +285,8 @@ getstring (int idx, sb *in, sb *acc)
        {
          int nest = 0;
          idx++;
-         while ((in->ptr[idx] != '>' || nest)
-                && idx < in->len)
+         while (idx < in->len
+                && (in->ptr[idx] != '>' || nest))
            {
              if (in->ptr[idx] == '!')
                {
@@ -382,13 +354,13 @@ getstring (int idx, sb *in, sb *acc)
 /* Fetch string from the input stream,
    rules:
     'Bxyx<whitespace>          -> return 'Bxyza
-    %<char>            -> return string of decimal value of x
-    "<string>"         -> return string
-    xyx<whitespace>     -> return xyz
-*/
+    %<expr>            -> return string of decimal value of <expr>
+    "string"           -> return string
+    (string)           -> return (string-including-whitespaces)
+    xyx<whitespace>     -> return xyz.  */
 
-static int
-get_any_string (int idx, sb *in, sb *out, int expand, int pretend_quoted)
+static size_t
+get_any_string (size_t idx, sb *in, sb *out)
 {
   sb_reset (out);
   idx = sb_skip_white (idx, in);
@@ -397,37 +369,33 @@ get_any_string (int idx, sb *in, sb *out, int expand, int pretend_quoted)
     {
       if (in->len > idx + 2 && in->ptr[idx + 1] == '\'' && ISBASE (in->ptr[idx]))
        {
-         while (!ISSEP (in->ptr[idx]))
+         while (idx < in->len && !ISSEP (in->ptr[idx]))
            sb_add_char (out, in->ptr[idx++]);
        }
-      else if (in->ptr[idx] == '%'
-              && macro_alternate
-              && expand)
+      else if (in->ptr[idx] == '%' && macro_alternate)
        {
-         int val;
-         char buf[20];
+         offsetT val;
+         char buf[64];
+
          /* Turns the next expression into a string.  */
          /* xgettext: no-c-format */
          idx = (*macro_expr) (_("% operator needs absolute expression"),
                               idx + 1,
                               in,
                               &val);
-         sprintf (buf, "%d", val);
+         sprintf (buf, "%" BFD_VMA_FMT "d", val);
          sb_add_string (out, buf);
        }
       else if (in->ptr[idx] == '"'
               || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
               || (macro_alternate && in->ptr[idx] == '\''))
        {
-         if (macro_alternate
-             && ! macro_strip_at
-             && expand)
+         if (macro_alternate && ! macro_strip_at && in->ptr[idx] != '<')
            {
              /* Keep the quotes.  */
-             sb_add_char (out, '\"');
-
+             sb_add_char (out, '"');
              idx = getstring (idx, in, out);
-             sb_add_char (out, '\"');
+             sb_add_char (out, '"');
            }
          else
            {
@@ -436,39 +404,98 @@ get_any_string (int idx, sb *in, sb *out, int expand, int pretend_quoted)
        }
       else
        {
+         char *br_buf = XNEWVEC (char, 1);
+         char *in_br = br_buf;
+
+         *in_br = '\0';
          while (idx < in->len
-                && (in->ptr[idx] == '"'
-                    || in->ptr[idx] == '\''
-                    || pretend_quoted
+                && (*in_br
                     || (in->ptr[idx] != ' '
-                        && in->ptr[idx] != '\t'
-                        && in->ptr[idx] != ','
-                        && (in->ptr[idx] != '<'
-                            || (! macro_alternate && ! macro_mri)))))
+                        && in->ptr[idx] != '\t'))
+                && in->ptr[idx] != ','
+                && (in->ptr[idx] != '<'
+                    || (! macro_alternate && ! macro_mri)))
            {
-             if (in->ptr[idx] == '"'
-                 || in->ptr[idx] == '\'')
+             char tchar = in->ptr[idx];
+
+             switch (tchar)
                {
-                 char tchar = in->ptr[idx];
+               case '"':
+               case '\'':
                  sb_add_char (out, in->ptr[idx++]);
                  while (idx < in->len
                         && in->ptr[idx] != tchar)
                    sb_add_char (out, in->ptr[idx++]);
                  if (idx == in->len)
-                   return idx;
+                   {
+                     free (br_buf);
+                     return idx;
+                   }
+                 break;
+               case '(':
+               case '[':
+                 if (in_br > br_buf)
+                   --in_br;
+                 else
+                   {
+                     br_buf = XNEWVEC (char, strlen (in_br) + 2);
+                     strcpy (br_buf + 1, in_br);
+                     free (in_br);
+                     in_br = br_buf;
+                   }
+                 *in_br = tchar;
+                 break;
+               case ')':
+                 if (*in_br == '(')
+                   ++in_br;
+                 break;
+               case ']':
+                 if (*in_br == '[')
+                   ++in_br;
+                 break;
                }
-             sb_add_char (out, in->ptr[idx++]);
+             sb_add_char (out, tchar);
+             ++idx;
            }
+         free (br_buf);
        }
     }
 
   return idx;
 }
 
+/* Allocate a new formal.  */
+
+static formal_entry *
+new_formal (void)
+{
+  formal_entry *formal;
+
+  formal = XNEW (formal_entry);
+
+  sb_new (&formal->name);
+  sb_new (&formal->def);
+  sb_new (&formal->actual);
+  formal->next = NULL;
+  formal->type = FORMAL_OPTIONAL;
+  return formal;
+}
+
+/* Free a formal.  */
+
+static void
+del_formal (formal_entry *formal)
+{
+  sb_kill (&formal->actual);
+  sb_kill (&formal->def);
+  sb_kill (&formal->name);
+  free (formal);
+}
+
 /* Pick up the formal parameters of a macro definition.  */
 
-static int
-do_formals (macro_entry *macro, int idx, sb *in)
+static size_t
+do_formals (macro_entry *macro, size_t idx, sb *in)
 {
   formal_entry **p = &macro->formals;
   const char *name;
@@ -476,43 +503,86 @@ do_formals (macro_entry *macro, int idx, sb *in)
   idx = sb_skip_white (idx, in);
   while (idx < in->len)
     {
-      formal_entry *formal;
-      int cidx;
-
-      formal = (formal_entry *) xmalloc (sizeof (formal_entry));
-
-      sb_new (&formal->name);
-      sb_new (&formal->def);
-      sb_new (&formal->actual);
+      formal_entry *formal = new_formal ();
+      size_t cidx;
+      formal_hash_entry_t *elt;
 
       idx = get_token (idx, in, &formal->name);
       if (formal->name.len == 0)
        {
          if (macro->formal_count)
            --idx;
+         del_formal (formal);  /* 'formal' goes out of scope.  */
          break;
        }
       idx = sb_skip_white (idx, in);
       /* This is a formal.  */
+      name = sb_terminate (&formal->name);
+      if (! macro_mri
+         && idx < in->len
+         && in->ptr[idx] == ':'
+         && (! is_name_beginner (':')
+             || idx + 1 >= in->len
+             || ! is_part_of_name (in->ptr[idx + 1])))
+       {
+         /* Got a qualifier.  */
+         sb qual;
+
+         sb_new (&qual);
+         idx = get_token (sb_skip_white (idx + 1, in), in, &qual);
+         sb_terminate (&qual);
+         if (qual.len == 0)
+           as_bad_where (macro->file,
+                         macro->line,
+                         _("Missing parameter qualifier for `%s' in macro `%s'"),
+                         name,
+                         macro->name);
+         else if (strcmp (qual.ptr, "req") == 0)
+           formal->type = FORMAL_REQUIRED;
+         else if (strcmp (qual.ptr, "vararg") == 0)
+           formal->type = FORMAL_VARARG;
+         else
+           as_bad_where (macro->file,
+                         macro->line,
+                         _("`%s' is not a valid parameter qualifier for `%s' in macro `%s'"),
+                         qual.ptr,
+                         name,
+                         macro->name);
+         sb_kill (&qual);
+         idx = sb_skip_white (idx, in);
+       }
       if (idx < in->len && in->ptr[idx] == '=')
        {
          /* Got a default.  */
-         idx = get_any_string (idx + 1, in, &formal->def, 1, 0);
+         idx = get_any_string (idx + 1, in, &formal->def);
          idx = sb_skip_white (idx, in);
+         if (formal->type == FORMAL_REQUIRED)
+           {
+             sb_reset (&formal->def);
+             as_warn_where (macro->file,
+                           macro->line,
+                           _("Pointless default value for required parameter `%s' in macro `%s'"),
+                           name,
+                           macro->name);
+           }
        }
 
       /* Add to macro's hash table.  */
-      name = sb_terminate (&formal->name);
-      if (! hash_find (macro->formal_hash, name))
-       hash_jam (macro->formal_hash, name, formal);
-      else
-       as_bad_where (macro->file,
-                     macro->line,
-                     _("A parameter named `%s' already exists for macro `%s'"),
-                     name,
-                     macro->name);
+      elt = formal_entry_alloc (name, formal);
+      if (htab_insert (macro->formal_hash, elt, 0) != NULL)
+       {
+         free (elt);
+         as_bad_where (macro->file, macro->line,
+                       _("A parameter named `%s' "
+                         "already exists for macro `%s'"),
+                       name, macro->name);
+       }
 
       formal->index = macro->formal_count++;
+      *p = formal;
+      p = &formal->next;
+      if (formal->type == FORMAL_VARARG)
+       break;
       cidx = idx;
       idx = sb_skip_comma (idx, in);
       if (idx != cidx && idx >= in->len)
@@ -520,25 +590,17 @@ do_formals (macro_entry *macro, int idx, sb *in)
          idx = cidx;
          break;
        }
-      *p = formal;
-      p = &formal->next;
-      *p = NULL;
     }
 
   if (macro_mri)
     {
-      formal_entry *formal;
+      formal_entry *formal = new_formal ();
+      formal_hash_entry_t *elt;
 
       /* Add a special NARG formal, which macro_expand will set to the
-         number of arguments.  */
-      formal = (formal_entry *) xmalloc (sizeof (formal_entry));
-
-      sb_new (&formal->name);
-      sb_new (&formal->def);
-      sb_new (&formal->actual);
-
+        number of arguments.  */
       /* The same MRI assemblers which treat '@' characters also use
-         the name $NARG.  At least until we find an exception.  */
+        the name $NARG.  At least until we find an exception.  */
       if (macro_strip_at)
        name = "$NARG";
       else
@@ -547,37 +609,57 @@ do_formals (macro_entry *macro, int idx, sb *in)
       sb_add_string (&formal->name, name);
 
       /* Add to macro's hash table.  */
-      if (hash_find (macro->formal_hash, name))
-       as_bad_where (macro->file,
-                     macro->line,
-                     _("Reserved word `%s' used as parameter in macro `%s'"),
-                     name,
-                     macro->name);
-      hash_jam (macro->formal_hash, name, formal);
+      elt = formal_entry_alloc (name, formal);
+      if (htab_insert (macro->formal_hash, elt, 0) != NULL)
+       {
+         free (elt);
+         as_bad_where (macro->file, macro->line,
+                       _("Reserved word `%s' used as parameter in macro `%s'"),
+                       name, macro->name);
+       }
 
       formal->index = NARG_INDEX;
       *p = formal;
-      formal->next = NULL;
     }
 
   return idx;
 }
 
+/* Free the memory allocated to a macro.  */
+
+static void
+free_macro (macro_entry *macro)
+{
+  formal_entry *formal;
+
+  for (formal = macro->formals; formal; )
+    {
+      formal_entry *f;
+
+      f = formal;
+      formal = formal->next;
+      del_formal (f);
+    }
+  htab_delete (macro->formal_hash);
+  sb_kill (&macro->sub);
+  free (macro);
+}
+
 /* Define a new macro.  Returns NULL on success, otherwise returns an
    error message.  If NAMEP is not NULL, *NAMEP is set to the name of
    the macro which was defined.  */
 
 const char *
-define_macro (int idx, sb *in, sb *label,
-             int (*get_line) (sb *),
-             char *file, unsigned int line,
+define_macro (size_t idx, sb *in, sb *label,
+             size_t (*get_line) (sb *),
+             const char *file, unsigned int line,
              const char **namep)
 {
   macro_entry *macro;
   sb name;
   const char *error = NULL;
 
-  macro = (macro_entry *) xmalloc (sizeof (macro_entry));
+  macro = XNEW (macro_entry);
   sb_new (&macro->sub);
   sb_new (&name);
   macro->file = file;
@@ -585,7 +667,8 @@ define_macro (int idx, sb *in, sb *label,
 
   macro->formal_count = 0;
   macro->formals = 0;
-  macro->formal_hash = hash_new ();
+  macro->formal_hash = htab_create_alloc (7, hash_formal_entry, eq_formal_entry,
+                                         NULL, xcalloc, free);
 
   idx = sb_skip_white (idx, in);
   if (! buffer_and_nest ("MACRO", "ENDM", &macro->sub, get_line))
@@ -611,7 +694,7 @@ define_macro (int idx, sb *in, sb *label,
     }
   else
     {
-      int cidx;
+      size_t cidx;
 
       idx = get_token (idx, in, &name);
       macro->name = sb_terminate (&name);
@@ -630,10 +713,15 @@ define_macro (int idx, sb *in, sb *label,
   /* And stick it in the macro hash table.  */
   for (idx = 0; idx < name.len; idx++)
     name.ptr[idx] = TOLOWER (name.ptr[idx]);
-  if (hash_find (macro_hash, macro->name))
-    error = _("Macro `%s' was already defined");
   if (!error)
-    error = hash_jam (macro_hash, macro->name, (PTR) macro);
+    {
+      macro_hash_entry_t *elt = macro_entry_alloc (macro->name, macro);
+      if (htab_insert (macro_hash, elt, 0) != NULL)
+       {
+         free (elt);
+         error = _("Macro `%s' was already defined");
+       }
+    }
 
   if (namep != NULL)
     *namep = macro->name;
@@ -648,8 +736,8 @@ define_macro (int idx, sb *in, sb *label,
 
 /* Scan a token, and then skip KIND.  */
 
-static int
-get_apost_token (int idx, sb *in, sb *name, int kind)
+static size_t
+get_apost_token (size_t idx, sb *in, sb *name, int kind)
 {
   idx = get_token (idx, in, name);
   if (idx < in->len
@@ -662,11 +750,11 @@ get_apost_token (int idx, sb *in, sb *name, int kind)
 
 /* Substitute the actual value for a formal parameter.  */
 
-static int
-sub_actual (int start, sb *in, sb *t, struct hash_control *formal_hash,
+static size_t
+sub_actual (size_t start, sb *in, sb *t, struct htab *formal_hash,
            int kind, sb *out, int copyifnotthere)
 {
-  int src;
+  size_t src;
   formal_entry *ptr;
 
   src = get_apost_token (start, in, t, kind);
@@ -677,7 +765,7 @@ sub_actual (int start, sb *in, sb *t, struct hash_control *formal_hash,
       && (src == start || in->ptr[src - 1] != '@'))
     ptr = NULL;
   else
-    ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (t));
+    ptr = formal_entry_find (formal_hash, sb_terminate (t));
   if (ptr)
     {
       if (ptr->actual.len)
@@ -694,6 +782,8 @@ sub_actual (int start, sb *in, sb *t, struct hash_control *formal_hash,
       /* Doing this permits people to use & in macro bodies.  */
       sb_add_char (out, '&');
       sb_add_sb (out, t);
+      if (src != start && in->ptr[src - 1] == '&')
+       sb_add_char (out, '&');
     }
   else if (copyifnotthere)
     {
@@ -711,10 +801,11 @@ sub_actual (int start, sb *in, sb *t, struct hash_control *formal_hash,
 
 static const char *
 macro_expand_body (sb *in, sb *out, formal_entry *formals,
-                  struct hash_control *formal_hash, const macro_entry *macro)
+                  struct htab *formal_hash, const macro_entry *macro)
 {
   sb t;
-  int src = 0, inquote = 0, macro_line = 0;
+  size_t src = 0;
+  int inquote = 0, macro_line = 0;
   formal_entry *loclist = NULL;
   const char *err = NULL;
 
@@ -734,9 +825,8 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
            }
          else
            {
-             /* FIXME: Why do we do this?  */
-             /* At least in alternate mode this seems correct; without this
-                one can't append a literal to a parameter.  */
+             /* Permit macro parameter substitution delineated with
+                an '&' prefix and optional '&' suffix.  */
              src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0);
            }
        }
@@ -762,7 +852,7 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
            {
              /* Sub in the macro invocation number.  */
 
-             char buffer[10];
+             char buffer[12];
              src++;
              sprintf (buffer, "%d", macro_number);
              sb_add_string (out, buffer);
@@ -814,7 +904,9 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
          if (! macro
              || src + 5 >= in->len
              || strncasecmp (in->ptr + src, "LOCAL", 5) != 0
-             || ! ISWHITE (in->ptr[src + 5]))
+             || ! ISWHITE (in->ptr[src + 5])
+             /* PR 11507: Skip keyword LOCAL if it is found inside a quoted string.  */
+             || inquote)
            {
              sb_reset (&t);
              src = sub_actual (src, in, &t, formal_hash,
@@ -827,38 +919,31 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
              while (in->ptr[src] != '\n')
                {
                  const char *name;
-                 formal_entry *f;
+                 formal_entry *f = new_formal ();
+                 formal_hash_entry_t *elt;
 
-                 f = (formal_entry *) xmalloc (sizeof (formal_entry));
-                 sb_new (&f->name);
                  src = get_token (src, in, &f->name);
                  name = sb_terminate (&f->name);
-                 if (! hash_find (formal_hash, name))
+                 elt = formal_entry_alloc (name, f);
+                 if (htab_insert (formal_hash, elt, 0) != NULL)
+                   {
+                     free (elt);
+                     as_bad_where (macro->file, macro->line + macro_line,
+                                   _("`%s' was already used as parameter "
+                                     "(or another local) name"), name);
+                     del_formal (f);
+                   }
+                 else
                    {
                      static int loccnt;
                      char buf[20];
 
-                     sb_new (&f->def);
-                     sb_new (&f->actual);
                      f->index = LOCAL_INDEX;
                      f->next = loclist;
                      loclist = f;
 
                      sprintf (buf, IS_ELF ? ".LL%04x" : "LL%04x", ++loccnt);
                      sb_add_string (&f->actual, buf);
-
-                     err = hash_jam (formal_hash, name, f);
-                     if (err != NULL)
-                       break;
-                   }
-                 else
-                   {
-                     as_bad_where (macro->file,
-                                   macro->line + macro_line,
-                                   _("`%s' was already used as parameter (or another local) name"),
-                                   name);
-                     sb_kill (&f->name);
-                     free (f);
                    }
 
                  src = sb_skip_comma (src, in);
@@ -890,17 +975,17 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
 
          sb_reset (&t);
          src = get_token (src + 2, in, &t);
-         ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (&t));
+         ptr = formal_entry_find (formal_hash, sb_terminate (&t));
          if (ptr == NULL)
            {
              /* FIXME: We should really return a warning string here,
-                 but we can't, because the == might be in the MRI
-                 comment field, and, since the nature of the MRI
-                 comment field depends upon the exact instruction
-                 being used, we don't have enough information here to
-                 figure out whether it is or not.  Instead, we leave
-                 the == in place, which should cause a syntax error if
-                 it is not in a comment.  */
+                but we can't, because the == might be in the MRI
+                comment field, and, since the nature of the MRI
+                comment field depends upon the exact instruction
+                being used, we don't have enough information here to
+                figure out whether it is or not.  Instead, we leave
+                the == in place, which should cause a syntax error if
+                it is not in a comment.  */
              sb_add_char (out, '=');
              sb_add_char (out, '=');
              sb_add_sb (out, &t);
@@ -930,15 +1015,13 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
   while (loclist != NULL)
     {
       formal_entry *f;
+      const char *name;
 
       f = loclist->next;
-      /* Setting the value to NULL effectively deletes the entry.  We
-         avoid calling hash_delete because it doesn't reclaim memory.  */
-      hash_jam (formal_hash, sb_terminate (&loclist->name), NULL);
-      sb_kill (&loclist->name);
-      sb_kill (&loclist->def);
-      sb_kill (&loclist->actual);
-      free (loclist);
+      name = sb_terminate (&loclist->name);
+      formal_hash_entry_t needle = { name, NULL };
+      htab_remove_elt (formal_hash, &needle);
+      del_formal (loclist);
       loclist = f;
     }
 
@@ -949,15 +1032,14 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
    body.  */
 
 static const char *
-macro_expand (int idx, sb *in, macro_entry *m, sb *out)
+macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
 {
   sb t;
   formal_entry *ptr;
   formal_entry *f;
-  int is_positional = 0;
   int is_keyword = 0;
   int narg = 0;
-  const char *err;
+  const char *err = NULL;
 
   sb_new (&t);
 
@@ -971,7 +1053,7 @@ macro_expand (int idx, sb *in, macro_entry *m, sb *out)
   if (macro_mri)
     {
       /* The macro may be called with an optional qualifier, which may
-         be referred to in the macro body as \0.  */
+        be referred to in the macro body as \0.  */
       if (idx < in->len && in->ptr[idx] == '.')
        {
          /* The Microtec assembler ignores this if followed by a white space.
@@ -981,18 +1063,14 @@ macro_expand (int idx, sb *in, macro_entry *m, sb *out)
                  && in->ptr[idx] != ' '
                  && in->ptr[idx] != '\t')
            {
-             formal_entry *n;
+             formal_entry *n = new_formal ();
 
-             n = (formal_entry *) xmalloc (sizeof (formal_entry));
-             sb_new (&n->name);
-             sb_new (&n->def);
-             sb_new (&n->actual);
              n->index = QUAL_INDEX;
 
              n->next = m->formals;
              m->formals = n;
 
-             idx = get_any_string (idx, in, &n->actual, 1, 0);
+             idx = get_any_string (idx, in, &n->actual);
            }
        }
     }
@@ -1001,7 +1079,7 @@ macro_expand (int idx, sb *in, macro_entry *m, sb *out)
   idx = sb_skip_white (idx, in);
   while (idx < in->len)
     {
-      int scan;
+      size_t scan;
 
       /* Look and see if it's a positional or keyword arg.  */
       scan = idx;
@@ -1021,27 +1099,43 @@ macro_expand (int idx, sb *in, macro_entry *m, sb *out)
          sb_reset (&t);
          idx = get_token (idx, in, &t);
          if (in->ptr[idx] != '=')
-           return _("confusion in formal parameters");
+           {
+             err = _("confusion in formal parameters");
+             break;
+           }
 
          /* Lookup the formal in the macro's list.  */
-         ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
+         ptr = formal_entry_find (m->formal_hash, sb_terminate (&t));
          if (!ptr)
-           return _("macro formal argument does not exist");
+           {
+             as_bad (_("Parameter named `%s' does not exist for macro `%s'"),
+                     t.ptr,
+                     m->name);
+             sb_reset (&t);
+             idx = get_any_string (idx + 1, in, &t);
+           }
          else
            {
              /* Insert this value into the right place.  */
-             sb_reset (&ptr->actual);
-             idx = get_any_string (idx + 1, in, &ptr->actual, 0, 0);
+             if (ptr->actual.len)
+               {
+                 as_warn (_("Value for parameter `%s' of macro `%s' was already specified"),
+                          ptr->name.ptr,
+                          m->name);
+                 sb_reset (&ptr->actual);
+               }
+             idx = get_any_string (idx + 1, in, &ptr->actual);
              if (ptr->actual.len > 0)
                ++narg;
            }
        }
       else
        {
-         /* This is a positional arg.  */
-         is_positional = 1;
          if (is_keyword)
-           return _("can't mix positional and keyword arguments");
+           {
+             err = _("can't mix positional and keyword arguments");
+             break;
+           }
 
          if (!f)
            {
@@ -1049,13 +1143,12 @@ macro_expand (int idx, sb *in, macro_entry *m, sb *out)
              int c;
 
              if (!macro_mri)
-               return _("too many positional arguments");
+               {
+                 err = _("too many positional arguments");
+                 break;
+               }
 
-             f = (formal_entry *) xmalloc (sizeof (formal_entry));
-             sb_new (&f->name);
-             sb_new (&f->def);
-             sb_new (&f->actual);
-             f->next = NULL;
+             f = new_formal ();
 
              c = -1;
              for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next)
@@ -1067,8 +1160,13 @@ macro_expand (int idx, sb *in, macro_entry *m, sb *out)
              f->index = c;
            }
 
-         sb_reset (&f->actual);
-         idx = get_any_string (idx, in, &f->actual, 1, 0);
+         if (f->type != FORMAL_VARARG)
+           idx = get_any_string (idx, in, &f->actual);
+         else
+           {
+             sb_add_buffer (&f->actual, in->ptr + idx, in->len - idx);
+             idx = in->len;
+           }
          if (f->actual.len > 0)
            ++narg;
          do
@@ -1089,19 +1187,29 @@ macro_expand (int idx, sb *in, macro_entry *m, sb *out)
        }
     }
 
-  if (macro_mri)
+  if (! err)
     {
-      char buffer[20];
-
-      sb_reset (&t);
-      sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG");
-      ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
-      sb_reset (&ptr->actual);
-      sprintf (buffer, "%d", narg);
-      sb_add_string (&ptr->actual, buffer);
-    }
+      for (ptr = m->formals; ptr; ptr = ptr->next)
+       {
+         if (ptr->type == FORMAL_REQUIRED && ptr->actual.len == 0)
+           as_bad (_("Missing value for required parameter `%s' of macro `%s'"),
+                   ptr->name.ptr,
+                   m->name);
+       }
 
-  err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m);
+      if (macro_mri)
+       {
+         char buffer[20];
+
+         sb_reset (&t);
+         sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG");
+         ptr = formal_entry_find (m->formal_hash, sb_terminate (&t));
+         sprintf (buffer, "%d", narg);
+         sb_add_string (&ptr->actual, buffer);
+       }
+
+      err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m);
+    }
 
   /* Discard any unnamed formal arguments.  */
   if (macro_mri)
@@ -1115,11 +1223,8 @@ macro_expand (int idx, sb *in, macro_entry *m, sb *out)
            pf = &(*pf)->next;
          else
            {
-             sb_kill (&(*pf)->name);
-             sb_kill (&(*pf)->def);
-             sb_kill (&(*pf)->actual);
              f = (*pf)->next;
-             free (*pf);
+             del_formal (*pf);
              *pf = f;
            }
        }
@@ -1140,7 +1245,7 @@ check_macro (const char *line, sb *expand,
             const char **error, macro_entry **info)
 {
   const char *s;
-  char *copy, *cs;
+  char *copy, *cls;
   macro_entry *macro;
   sb line_sb;
 
@@ -1154,13 +1259,12 @@ check_macro (const char *line, sb *expand,
   if (is_name_ender (*s))
     ++s;
 
-  copy = (char *) alloca (s - line + 1);
-  memcpy (copy, line, s - line);
-  copy[s - line] = '\0';
-  for (cs = copy; *cs != '\0'; cs++)
-    *cs = TOLOWER (*cs);
+  copy = xmemdup0 (line, s - line);
+  for (cls = copy; *cls != '\0'; cls ++)
+    *cls = TOLOWER (*cls);
 
-  macro = (macro_entry *) hash_find (macro_hash, copy);
+  macro = macro_entry_find (macro_hash, copy);
+  free (copy);
 
   if (macro == NULL)
     return 0;
@@ -1182,29 +1286,6 @@ check_macro (const char *line, sb *expand,
   return 1;
 }
 
-/* Free the memory allocated to a macro.  */
-
-static void
-free_macro(macro_entry *macro)
-{
-  formal_entry *formal;
-
-  for (formal = macro->formals; formal; )
-    {
-      void *ptr;
-
-      sb_kill (&formal->name);
-      sb_kill (&formal->def);
-      sb_kill (&formal->actual);
-      ptr = formal;
-      formal = formal->next;
-      free (ptr);
-    }
-  hash_die (macro->formal_hash);
-  sb_kill (&macro->sub);
-  free (macro);
-}
-
 /* Delete a macro.  */
 
 void
@@ -1212,22 +1293,26 @@ delete_macro (const char *name)
 {
   char *copy;
   size_t i, len;
-  macro_entry *macro;
+  void **slot;
+  macro_hash_entry_t needle;
 
   len = strlen (name);
-  copy = (char *) alloca (len + 1);
+  copy = XNEWVEC (char, len + 1);
   for (i = 0; i < len; ++i)
     copy[i] = TOLOWER (name[i]);
   copy[i] = '\0';
 
-  /* Since hash_delete doesn't free memory, just clear out the entry.  */
-  if ((macro = hash_find (macro_hash, copy)) != NULL)
+  needle.name = copy;
+  needle.macro = NULL;
+  slot = htab_find_slot (macro_hash, &needle, NO_INSERT);
+  if (slot)
     {
-      hash_jam (macro_hash, copy, NULL);
-      free_macro (macro);
+      free_macro (((macro_hash_entry_t *) *slot)->macro);
+      htab_clear_slot (macro_hash, slot);
     }
   else
-    as_warn (_("Attempt to purge non-existant macro `%s'"), copy);
+    as_warn (_("Attempt to purge non-existing macro `%s'"), copy);
+  free (copy);
 }
 
 /* Handle the MRI IRP and IRPC pseudo-ops.  These are handled as a
@@ -1235,12 +1320,12 @@ delete_macro (const char *name)
    success, or an error message otherwise.  */
 
 const char *
-expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *))
+expand_irp (int irpc, size_t idx, sb *in, sb *out, size_t (*get_line) (sb *))
 {
   sb sub;
   formal_entry f;
-  struct hash_control *h;
-  const char *err;
+  struct htab *h;
+  const char *err = NULL;
 
   idx = sb_skip_white (idx, in);
 
@@ -1256,13 +1341,14 @@ expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *))
   if (f.name.len == 0)
     return _("missing model parameter");
 
-  h = hash_new ();
-  err = hash_jam (h, sb_terminate (&f.name), &f);
-  if (err != NULL)
-    return err;
+  h = htab_create_alloc (16, hash_formal_entry, eq_formal_entry,
+                        NULL, xcalloc, free);
+
+  htab_insert (h, formal_entry_alloc (sb_terminate (&f.name), &f), 0);
 
   f.index = 1;
   f.next = NULL;
+  f.type = FORMAL_OPTIONAL;
 
   sb_reset (out);
 
@@ -1274,17 +1360,26 @@ expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *))
     }
   else
     {
+      bfd_boolean in_quotes = FALSE;
+
       if (irpc && in->ptr[idx] == '"')
-       ++idx;
+       {
+         in_quotes = TRUE;
+         ++idx;
+       }
+
       while (idx < in->len)
        {
          if (!irpc)
-           idx = get_any_string (idx, in, &f.actual, 1, 0);
+           idx = get_any_string (idx, in, &f.actual);
          else
            {
              if (in->ptr[idx] == '"')
                {
-                 int nxt;
+                 size_t nxt;
+
+                 if (irpc)
+                   in_quotes = ! in_quotes;
 
                  nxt = sb_skip_white (idx + 1, in);
                  if (nxt >= in->len)
@@ -1297,17 +1392,21 @@ expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *))
              sb_add_char (&f.actual, in->ptr[idx]);
              ++idx;
            }
+
          err = macro_expand_body (&sub, out, &f, h, 0);
          if (err != NULL)
            break;
          if (!irpc)
            idx = sb_skip_comma (idx, in);
-         else
+         else if (! in_quotes)
            idx = sb_skip_white (idx, in);
        }
     }
 
-  hash_die (h);
+  htab_delete (h);
+  sb_kill (&f.actual);
+  sb_kill (&f.def);
+  sb_kill (&f.name);
   sb_kill (&sub);
 
   return err;