Add testcase for generation of 32/64_PCREL.
[binutils-gdb.git] / gas / macro.c
index 6c0e554886a08ecb86e5023e8e9359649d408fd5..c5959f674dd320423a180338ba2bcd109080b77e 100644 (file)
@@ -1,5 +1,5 @@
 /* macro.c - macro support for gas
-   Copyright (C) 1994-2018 Free Software Foundation, Inc.
+   Copyright (C) 1994-2023 Free Software Foundation, Inc.
 
    Written by Steve and Judy Chamberlain of Cygnus Support,
       sac@cygnus.com
@@ -34,7 +34,7 @@
 #define ISSEP(x) \
  ((x) == ' ' || (x) == '\t' || (x) == ',' || (x) == '"' || (x) == ';' \
   || (x) == ')' || (x) == '(' \
-  || ((macro_alternate || macro_mri) && ((x) == '<' || (x) == '>')))
+  || ((flag_macro_alternate || flag_mri) && ((x) == '<' || (x) == '>')))
 
 #define ISBASE(x) \
   ((x) == 'b' || (x) == 'B' \
 
 /* The macro hash table.  */
 
-struct hash_control *macro_hash;
+htab_t macro_hash;
 
 /* Whether any macros have been defined.  */
 
 int macro_defined;
 
-/* Whether we are in alternate syntax mode.  */
-
-static int macro_alternate;
-
-/* Whether we are in MRI mode.  */
-
-static int macro_mri;
-
 /* Whether we should strip '@' characters.  */
 
-static int macro_strip_at;
-
-/* Function to use to parse an expression.  */
-
-static size_t (*macro_expr) (const char *, size_t, sb *, offsetT *);
+#define macro_strip_at false
 
 /* Number of macro expansions that have been done.  */
 
 static int macro_number;
 
-/* Initialize macro processing.  */
+static void free_macro (macro_entry *);
 
-void
-macro_init (int alternate, int mri, int strip_at,
-           size_t (*exp) (const char *, size_t, sb *, offsetT *))
+static void
+macro_del_f (void *ent)
 {
-  macro_hash = hash_new ();
-  macro_defined = 0;
-  macro_alternate = alternate;
-  macro_mri = mri;
-  macro_strip_at = strip_at;
-  macro_expr = exp;
+  string_tuple_t *tuple = ent;
+  free_macro ((macro_entry *) tuple->value);
 }
 
-/* Switch in and out of alternate mode on the fly.  */
+/* Initialize macro processing.  */
 
 void
-macro_set_alternate (int alternate)
+macro_init (void)
 {
-  macro_alternate = alternate;
+  macro_hash = htab_create_alloc (16, hash_string_tuple, eq_string_tuple,
+                                 macro_del_f, notes_calloc, NULL);
+  macro_defined = 0;
 }
 
-/* Switch in and out of MRI mode on the fly.  */
-
 void
-macro_mri_mode (int mri)
+macro_end (void)
 {
-  macro_mri = mri;
+  htab_delete (macro_hash);
 }
 
 /* Read input lines till we get to a TO string.
@@ -114,8 +97,7 @@ buffer_and_nest (const char *from, const char *to, sb *ptr,
   size_t from_len;
   size_t to_len = strlen (to);
   int depth = 1;
-  size_t line_start = ptr->len;
-  size_t more = get_line (ptr);
+  size_t line_start, more;
 
   if (to_len == 4 && strcasecmp (to, "ENDR") == 0)
     {
@@ -125,11 +107,29 @@ buffer_and_nest (const char *from, const char *to, sb *ptr,
   else
     from_len = strlen (from);
 
+  /* Record the present source position, such that diagnostics and debug info
+     can be properly associated with the respective original lines, rather
+     than with the line of the ending directive (TO).  */
+  {
+    unsigned int line;
+    char *linefile;
+
+    as_where_top (&line);
+    if (!flag_m68k_mri)
+      linefile = xasprintf ("\t.linefile %u .", line + 1);
+    else
+      linefile = xasprintf ("\tlinefile %u .", line + 1);
+    sb_add_string (ptr, linefile);
+    xfree (linefile);
+  }
+
+  line_start = ptr->len;
+  more = get_line (ptr);
   while (more)
     {
       /* Try to find the first pseudo op on the line.  */
       size_t i = line_start;
-      bfd_boolean had_colon = FALSE;
+      bool had_colon = false;
 
       /* With normal syntax we can suck what we want till we get
         to the dot.  With the alternate, labels have to start in
@@ -170,7 +170,7 @@ buffer_and_nest (const char *from, const char *to, sb *ptr,
            }
          i++;
          line_start = i;
-         had_colon = TRUE;
+         had_colon = true;
        }
 
       /* Skip trailing whitespace.  */
@@ -179,27 +179,39 @@ buffer_and_nest (const char *from, const char *to, sb *ptr,
 
       if (i < ptr->len && (ptr->ptr[i] == '.'
                           || NO_PSEUDO_DOT
-                          || macro_mri))
+                          || flag_mri))
        {
          if (! flag_m68k_mri && ptr->ptr[i] == '.')
            i++;
-         if (from == NULL
-            && strncasecmp (ptr->ptr + i, "IRPC", from_len = 4) != 0
-            && strncasecmp (ptr->ptr + i, "IRP", from_len = 3) != 0
-            && strncasecmp (ptr->ptr + i, "IREPC", from_len = 5) != 0
-            && strncasecmp (ptr->ptr + i, "IREP", from_len = 4) != 0
-            && strncasecmp (ptr->ptr + i, "REPT", from_len = 4) != 0
-            && strncasecmp (ptr->ptr + i, "REP", from_len = 3) != 0)
-           from_len = 0;
+         size_t len = ptr->len - i;
+         if (from == NULL)
+           {
+             if (len >= 5 && strncasecmp (ptr->ptr + i, "IREPC", 5) == 0)
+               from_len = 5;
+             else if (len >= 4 && strncasecmp (ptr->ptr + i, "IREP", 4) == 0)
+               from_len = 4;
+             else if (len >= 4 && strncasecmp (ptr->ptr + i, "IRPC", 4) == 0)
+               from_len = 4;
+             else if (len >= 4 && strncasecmp (ptr->ptr + i, "REPT", 4) == 0)
+               from_len = 4;
+             else if (len >= 3 && strncasecmp (ptr->ptr + i, "IRP", 3) == 0)
+               from_len = 3;
+             else if (len >= 3 && strncasecmp (ptr->ptr + i, "REP", 3) == 0)
+               from_len = 3;
+             else
+               from_len = 0;
+           }
          if ((from != NULL
-              ? strncasecmp (ptr->ptr + i, from, from_len) == 0
+              ? (len >= from_len
+                 && strncasecmp (ptr->ptr + i, from, from_len) == 0)
               : from_len > 0)
-             && (ptr->len == (i + from_len)
+             && (len == from_len
                  || ! (is_part_of_name (ptr->ptr[i + from_len])
                        || is_name_ender (ptr->ptr[i + from_len]))))
            depth++;
-         if (strncasecmp (ptr->ptr + i, to, to_len) == 0
-             && (ptr->len == (i + to_len)
+         if (len >= to_len
+             && strncasecmp (ptr->ptr + i, to, to_len) == 0
+             && (len == to_len
                  || ! (is_part_of_name (ptr->ptr[i + to_len])
                        || is_name_ender (ptr->ptr[i + to_len]))))
            {
@@ -213,25 +225,18 @@ buffer_and_nest (const char *from, const char *to, sb *ptr,
            }
 
          /* 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)
+            Apply .linefile directives that appear within the macro, alongside
+            keeping them for later expansion of the macro.  */
+         if (from != NULL && strcasecmp (from, "MACRO") == 0
+             && len >= 8 && strncasecmp (ptr->ptr + i, "linefile", 8) == 0)
            {
-             char *saved_input_line_pointer = input_line_pointer;
-             char saved_eol_char = ptr->ptr[ptr->len];
-
-             ptr->ptr[ptr->len] = '\0';
-             input_line_pointer = ptr->ptr + i + 8;
-             s_app_line (0);
-             ptr->ptr[ptr->len] = saved_eol_char;
-             input_line_pointer = saved_input_line_pointer;
-             ptr->len = line_start;
+             sb_add_char (ptr, more);
+             temp_ilp (sb_terminate (ptr) + i + 8);
+             s_linefile (0);
+             restore_ilp ();
+             line_start = ptr->len;
+             more = get_line (ptr);
+             continue;
            }
        }
 
@@ -266,7 +271,7 @@ get_token (size_t idx, sb *in, sb *name)
        }
     }
   /* Ignore trailing &.  */
-  if (macro_alternate && idx < in->len && in->ptr[idx] == '&')
+  if (flag_macro_alternate && idx < in->len && in->ptr[idx] == '&')
     idx++;
   return idx;
 }
@@ -278,15 +283,15 @@ getstring (size_t idx, sb *in, sb *acc)
 {
   while (idx < in->len
         && (in->ptr[idx] == '"'
-            || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
-            || (in->ptr[idx] == '\'' && macro_alternate)))
+            || (in->ptr[idx] == '<' && (flag_macro_alternate || flag_mri))
+            || (in->ptr[idx] == '\'' && flag_macro_alternate)))
     {
       if (in->ptr[idx] == '<')
        {
          int nest = 0;
          idx++;
-         while ((in->ptr[idx] != '>' || nest)
-                && idx < in->len)
+         while (idx < in->len
+                && (in->ptr[idx] != '>' || nest))
            {
              if (in->ptr[idx] == '!')
                {
@@ -318,7 +323,7 @@ getstring (size_t idx, sb *in, sb *acc)
              else
                escaped = 0;
 
-             if (macro_alternate && in->ptr[idx] == '!')
+             if (flag_macro_alternate && in->ptr[idx] == '!')
                {
                  idx ++;
 
@@ -369,28 +374,33 @@ get_any_string (size_t idx, sb *in, sb *out)
     {
       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)
+      else if (in->ptr[idx] == '%' && flag_macro_alternate)
        {
-         offsetT val;
-         char buf[20];
-
-         /* Turns the next expression into a string.  */
-         /* xgettext: no-c-format */
-         idx = (*macro_expr) (_("% operator needs absolute expression"),
-                              idx + 1,
-                              in,
-                              &val);
-         sprintf (buf, "%" BFD_VMA_FMT "d", val);
+         /* Turn the following expression into a string.  */
+         expressionS ex;
+         char buf[64];
+
+         sb_terminate (in);
+
+         temp_ilp (in->ptr + idx + 1);
+         expression_and_evaluate (&ex);
+         idx = input_line_pointer - in->ptr;
+         restore_ilp ();
+
+         if (ex.X_op != O_constant)
+           as_bad (_("%% operator needs absolute expression"));
+
+         sprintf (buf, "%" PRId64, (int64_t) ex.X_add_number);
          sb_add_string (out, buf);
        }
       else if (in->ptr[idx] == '"'
-              || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
-              || (macro_alternate && in->ptr[idx] == '\''))
+              || (in->ptr[idx] == '<' && (flag_macro_alternate || flag_mri))
+              || (flag_macro_alternate && in->ptr[idx] == '\''))
        {
-         if (macro_alternate && ! macro_strip_at && in->ptr[idx] != '<')
+         if (flag_macro_alternate && ! macro_strip_at && in->ptr[idx] != '<')
            {
              /* Keep the quotes.  */
              sb_add_char (out, '"');
@@ -414,7 +424,7 @@ get_any_string (size_t idx, sb *in, sb *out)
                         && in->ptr[idx] != '\t'))
                 && in->ptr[idx] != ','
                 && (in->ptr[idx] != '<'
-                    || (! macro_alternate && ! macro_mri)))
+                    || (! flag_macro_alternate && ! flag_mri)))
            {
              char tchar = in->ptr[idx];
 
@@ -517,7 +527,7 @@ do_formals (macro_entry *macro, size_t idx, sb *in)
       idx = sb_skip_white (idx, in);
       /* This is a formal.  */
       name = sb_terminate (&formal->name);
-      if (! macro_mri
+      if (! flag_mri
          && idx < in->len
          && in->ptr[idx] == ':'
          && (! is_name_beginner (':')
@@ -567,14 +577,13 @@ do_formals (macro_entry *macro, size_t idx, sb *in)
        }
 
       /* Add to macro's hash table.  */
-      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);
+      if (str_hash_insert (macro->formal_hash, name, formal, 0) != NULL)
+       {
+         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;
@@ -590,7 +599,7 @@ do_formals (macro_entry *macro, size_t idx, sb *in)
        }
     }
 
-  if (macro_mri)
+  if (flag_mri)
     {
       formal_entry *formal = new_formal ();
 
@@ -606,13 +615,12 @@ do_formals (macro_entry *macro, size_t 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);
+      if (str_hash_insert (macro->formal_hash, name, formal, 0) != NULL)
+       {
+         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;
@@ -636,36 +644,32 @@ free_macro (macro_entry *macro)
       formal = formal->next;
       del_formal (f);
     }
-  hash_die (macro->formal_hash);
+  htab_delete (macro->formal_hash);
   sb_kill (&macro->sub);
+  free ((char *) macro->name);
   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.  */
+/* Define a new macro.  */
 
-const char *
-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 *
+define_macro (sb *in, sb *label, size_t (*get_line) (sb *))
 {
   macro_entry *macro;
   sb name;
+  size_t idx;
   const char *error = NULL;
 
   macro = XNEW (macro_entry);
   sb_new (&macro->sub);
   sb_new (&name);
-  macro->file = file;
-  macro->line = line;
+  macro->file = as_where (&macro->line);
 
   macro->formal_count = 0;
   macro->formals = 0;
-  macro->formal_hash = hash_new_sized (7);
+  macro->formal_hash = str_htab_create ();
 
-  idx = sb_skip_white (idx, in);
+  idx = sb_skip_white (0, in);
   if (! buffer_and_nest ("MACRO", "ENDM", &macro->sub, get_line))
     error = _("unexpected end of file in macro `%s' definition");
   if (label != NULL && label->len != 0)
@@ -708,20 +712,22 @@ define_macro (size_t 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, (void *) macro);
-
-  if (namep != NULL)
-    *namep = macro->name;
+    {
+      if (str_hash_insert (macro_hash, macro->name, macro, 0) != NULL)
+       error = _("Macro `%s' was already defined");
+    }
 
   if (!error)
     macro_defined = 1;
   else
-    free_macro (macro);
+    {
+      as_bad_where (macro->file, macro->line, error, macro->name);
+      free_macro (macro);
+      macro = NULL;
+    }
 
-  return error;
+  return macro;
 }
 
 /* Scan a token, and then skip KIND.  */
@@ -732,7 +738,7 @@ get_apost_token (size_t idx, sb *in, sb *name, int kind)
   idx = get_token (idx, in, name);
   if (idx < in->len
       && in->ptr[idx] == kind
-      && (! macro_mri || macro_strip_at)
+      && (! flag_mri || macro_strip_at)
       && (! macro_strip_at || kind == '@'))
     idx++;
   return idx;
@@ -741,7 +747,7 @@ get_apost_token (size_t idx, sb *in, sb *name, int kind)
 /* Substitute the actual value for a formal parameter.  */
 
 static size_t
-sub_actual (size_t start, sb *in, sb *t, struct hash_control *formal_hash,
+sub_actual (size_t start, sb *in, sb *t, struct htab *formal_hash,
            int kind, sb *out, int copyifnotthere)
 {
   size_t src;
@@ -755,7 +761,7 @@ sub_actual (size_t 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 = str_hash_find (formal_hash, sb_terminate (t));
   if (ptr)
     {
       if (ptr->actual.len)
@@ -791,7 +797,7 @@ sub_actual (size_t 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;
   size_t src = 0;
@@ -806,7 +812,7 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
       if (in->ptr[src] == '&')
        {
          sb_reset (&t);
-         if (macro_mri)
+         if (flag_mri)
            {
              if (src + 1 < in->len && in->ptr[src + 1] == '&')
                src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1);
@@ -855,7 +861,7 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
              sb_add_char (out, '&');
              src++;
            }
-         else if (macro_mri && src < in->len && ISALNUM (in->ptr[src]))
+         else if (flag_mri && src < in->len && ISALNUM (in->ptr[src]))
            {
              int ind;
              formal_entry *f;
@@ -885,7 +891,7 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
              src = sub_actual (src, in, &t, formal_hash, '\'', out, 0);
            }
        }
-      else if ((macro_alternate || macro_mri)
+      else if ((flag_macro_alternate || flag_mri)
               && is_name_beginner (in->ptr[src])
               && (! inquote
                   || ! macro_strip_at
@@ -913,7 +919,14 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
 
                  src = get_token (src, in, &f->name);
                  name = sb_terminate (&f->name);
-                 if (! hash_find (formal_hash, name))
+                 if (str_hash_insert (formal_hash, name, f, 0) != NULL)
+                   {
+                     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];
@@ -924,18 +937,6 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
 
                      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);
-                     del_formal (f);
                    }
 
                  src = sb_skip_comma (src, in);
@@ -943,7 +944,7 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
            }
        }
       else if (in->ptr[src] == '"'
-              || (macro_mri && in->ptr[src] == '\''))
+              || (flag_mri && in->ptr[src] == '\''))
        {
          inquote = !inquote;
          sb_add_char (out, in->ptr[src++]);
@@ -958,7 +959,7 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
              ++src;
            }
        }
-      else if (macro_mri
+      else if (flag_mri
               && in->ptr[src] == '='
               && src + 1 < in->len
               && in->ptr[src + 1] == '=')
@@ -967,7 +968,7 @@ 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 = str_hash_find (formal_hash, sb_terminate (&t));
          if (ptr == NULL)
            {
              /* FIXME: We should really return a warning string here,
@@ -1011,11 +1012,13 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
 
       f = loclist->next;
       name = sb_terminate (&loclist->name);
-      hash_delete (formal_hash, name, f == NULL);
+      str_hash_delete (formal_hash, name);
       del_formal (loclist);
       loclist = f;
     }
 
+  if (!err && (out->len == 0 || out->ptr[out->len - 1] != '\n'))
+    sb_add_char (out, '\n');
   return err;
 }
 
@@ -1041,7 +1044,7 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
   while (f != NULL && f->index < 0)
     f = f->next;
 
-  if (macro_mri)
+  if (flag_mri)
     {
       /* The macro may be called with an optional qualifier, which may
         be referred to in the macro body as \0.  */
@@ -1076,10 +1079,10 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
       scan = idx;
       while (scan < in->len
             && !ISSEP (in->ptr[scan])
-            && !(macro_mri && in->ptr[scan] == '\'')
-            && (!macro_alternate && in->ptr[scan] != '='))
+            && !(flag_mri && in->ptr[scan] == '\'')
+            && (!flag_macro_alternate && in->ptr[scan] != '='))
        scan++;
-      if (scan < in->len && !macro_alternate && in->ptr[scan] == '=')
+      if (scan < in->len && !flag_macro_alternate && in->ptr[scan] == '=')
        {
          is_keyword = 1;
 
@@ -1089,14 +1092,14 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
             then the actual stuff.  */
          sb_reset (&t);
          idx = get_token (idx, in, &t);
-         if (in->ptr[idx] != '=')
+         if (idx >= in->len || in->ptr[idx] != '=')
            {
              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 = str_hash_find (m->formal_hash, sb_terminate (&t));
          if (!ptr)
            {
              as_bad (_("Parameter named `%s' does not exist for macro `%s'"),
@@ -1133,7 +1136,7 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
              formal_entry **pf;
              int c;
 
-             if (!macro_mri)
+             if (!flag_mri)
                {
                  err = _("too many positional arguments");
                  break;
@@ -1153,7 +1156,7 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
 
          if (f->type != FORMAL_VARARG)
            idx = get_any_string (idx, in, &f->actual);
-         else
+         else if (idx < in->len)
            {
              sb_add_buffer (&f->actual, in->ptr + idx, in->len - idx);
              idx = in->len;
@@ -1167,13 +1170,13 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
          while (f != NULL && f->index < 0);
        }
 
-      if (! macro_mri)
+      if (! flag_mri)
        idx = sb_skip_comma (idx, in);
       else
        {
-         if (in->ptr[idx] == ',')
+         if (idx < in->len && in->ptr[idx] == ',')
            ++idx;
-         if (ISWHITE (in->ptr[idx]))
+         if (idx < in->len && ISWHITE (in->ptr[idx]))
            break;
        }
     }
@@ -1188,22 +1191,23 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
                    m->name);
        }
 
-      if (macro_mri)
+      if (flag_mri)
        {
-         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));
-         sprintf (buffer, "%d", narg);
-         sb_add_string (&ptr->actual, buffer);
+         ptr = str_hash_find (m->formal_hash,
+                              macro_strip_at ? "$NARG" : "NARG");
+         if (ptr)
+           {
+             char buffer[20];
+             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)
+  if (flag_mri)
     {
       formal_entry **pf;
 
@@ -1241,7 +1245,7 @@ check_macro (const char *line, sb *expand,
   sb line_sb;
 
   if (! is_name_beginner (*line)
-      && (! macro_mri || *line != '.'))
+      && (! flag_mri || *line != '.'))
     return 0;
 
   s = line + 1;
@@ -1254,7 +1258,7 @@ check_macro (const char *line, sb *expand,
   for (cls = copy; *cls != '\0'; cls ++)
     *cls = TOLOWER (*cls);
 
-  macro = (macro_entry *) hash_find (macro_hash, copy);
+  macro = str_hash_find (macro_hash, copy);
   free (copy);
 
   if (macro == NULL)
@@ -1292,14 +1296,9 @@ delete_macro (const char *name)
     copy[i] = TOLOWER (name[i]);
   copy[i] = '\0';
 
-  /* We can only ask hash_delete to free memory if we are deleting
-     macros in reverse order to their definition.
-     So just clear out the entry.  */
-  if ((macro = (macro_entry *) hash_find (macro_hash, copy)) != NULL)
-    {
-      hash_jam (macro_hash, copy, NULL);
-      free_macro (macro);
-    }
+  macro = str_hash_find (macro_hash, copy);
+  if (macro != NULL)
+    str_hash_delete (macro_hash, copy);
   else
     as_warn (_("Attempt to purge non-existing macro `%s'"), copy);
   free (copy);
@@ -1314,14 +1313,17 @@ 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);
 
   sb_new (&sub);
   if (! buffer_and_nest (NULL, "ENDR", &sub, get_line))
-    return _("unexpected end of file in irp or irpc");
+    {
+      err = _("unexpected end of file in irp or irpc");
+      goto out2;
+    }
 
   sb_new (&f.name);
   sb_new (&f.def);
@@ -1329,12 +1331,14 @@ expand_irp (int irpc, size_t idx, sb *in, sb *out, size_t (*get_line) (sb *))
 
   idx = get_token (idx, in, &f.name);
   if (f.name.len == 0)
-    return _("missing model parameter");
+    {
+      err = _("missing model parameter");
+      goto out1;
+    }
+
+  h = str_htab_create ();
 
-  h = hash_new ();
-  err = hash_jam (h, sb_terminate (&f.name), &f);
-  if (err != NULL)
-    return err;
+  str_hash_insert (h, sb_terminate (&f.name), &f, 0);
 
   f.index = 1;
   f.next = NULL;
@@ -1350,11 +1354,11 @@ expand_irp (int irpc, size_t idx, sb *in, sb *out, size_t (*get_line) (sb *))
     }
   else
     {
-      bfd_boolean in_quotes = FALSE;
+      bool in_quotes = false;
 
       if (irpc && in->ptr[idx] == '"')
        {
-         in_quotes = TRUE;
+         in_quotes = true;
          ++idx;
        }
 
@@ -1393,10 +1397,12 @@ expand_irp (int irpc, size_t idx, sb *in, sb *out, size_t (*get_line) (sb *))
        }
     }
 
-  hash_die (h);
+  htab_delete (h);
+ out1:
   sb_kill (&f.actual);
   sb_kill (&f.def);
   sb_kill (&f.name);
+ out2:
   sb_kill (&sub);
 
   return err;