[binutils-gdb] Fix the handling or arguments and macro pseudo-variables inside nested assembler macros.
Nick Clifton
nickc@sourceware.org
Thu Dec 19 10:01:35 GMT 2024
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9f2e3c21f6506f081f1360f02b847606e0e00995
commit 9f2e3c21f6506f081f1360f02b847606e0e00995
Author: Nick Clifton <nickc@redhat.com>
Date: Thu Dec 19 09:59:11 2024 +0000
Fix the handling or arguments and macro pseudo-variables inside nested assembler macros.
PR 32391
Diff:
---
gas/config/tc-iq2000.c | 6 +-
gas/doc/as.texi | 60 +++++++-
gas/input-scrub.c | 7 +-
gas/macro.c | 270 ++++++++++++++++++++++++++++++------
gas/macro.h | 17 +--
gas/read.c | 15 +-
gas/testsuite/gas/macros/macros.exp | 1 +
gas/testsuite/gas/macros/nesting.d | 28 ++++
gas/testsuite/gas/macros/nesting.s | 104 ++++++++++++++
9 files changed, 444 insertions(+), 64 deletions(-)
diff --git a/gas/config/tc-iq2000.c b/gas/config/tc-iq2000.c
index 2198ffda2b9..1cbc578281f 100644
--- a/gas/config/tc-iq2000.c
+++ b/gas/config/tc-iq2000.c
@@ -105,8 +105,6 @@ struct iq2000_hi_fixup
/* The list of unmatched HI relocs. */
static struct iq2000_hi_fixup * iq2000_hi_fixup_list;
-/* Macro hash table, which we will add to. */
-extern struct htab *macro_hash;
const char md_shortopts[] = "";
const struct option md_longopts[] =
@@ -279,9 +277,7 @@ iq2000_add_macro (const char * name,
}
}
- str_hash_insert (macro_hash, macro->name, macro, 1);
-
- macro_defined = 1;
+ (void) add_macro (macro, true);
}
static void
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 6be54b57599..1683594a0bb 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -6215,7 +6215,12 @@ With that definition, @samp{SUM 0,5} is equivalent to this assembly input:
@item .macro @var{macname}
@itemx .macro @var{macname} @var{macargs} @dots{}
@cindex @code{macro} directive
-Begin the definition of a macro called @var{macname}. If your macro
+Begin the definition of a macro called @var{macname}. Macro names are case
+insensitive. Macro definitions can be nested, although their behaviour is
+sometimes counter intuitive. Nested macros only have scope within their
+defining macro.
+
+If your macro
definition requires arguments, specify their names after the macro name,
separated by commas or spaces. You can qualify the macro argument to
indicate whether all invocations must specify a non-blank value (through
@@ -6380,6 +6385,29 @@ adjacent string literals - even if separated only by a blank - will not be
concatenated when determining macro arguments, even if they're only separated
by white space. This is unlike certain other pseudo ops, e.g. @code{.ascii}.
+Nested macros can access the arguments of their parents. But also if their
+argument names clash with those of their parents, their versions are used. So
+for example:
+
+@smallexample
+.macro OUTER arg1, arg2, arg3:vararg
+ .macro INNER arg4 arg2
+ .dc.a \arg2
+ .dc.a \arg3
+ .endm
+ INNER \arg1 bert
+ .dc.a \arg2
+.endm
+
+OUTER fred, jim, harry\arg4
+@end smallexample
+
+This will generate references to symbols called @samp{jim} - from the
+definition of the OUTER macro, @samp{bert} - from the definition in INNER
+where arg2 has been overridden and @samp{harryfred} - from the definition in
+INNER where the value of arg3 from OUTER is used, but with the value of arg4
+substituted into the symbol.
+
@item .endm
@cindex @code{endm} directive
Mark the end of a macro definition.
@@ -6395,6 +6423,33 @@ Exit early from the current macro definition.
executed in this pseudo-variable; you can copy that number to your
output with @samp{\@@}, but @emph{only within a macro definition}.
+Note - the @samp{\@@} counter is incremented at the end of the expansion of a
+macro, but before the contents of any nested macros are evaluated. This can
+lead to counter-intuitive behaviour when nested macros are used. For example:
+
+@smallexample
+ .macro o
+ .macro i
+ _i\@@_:
+ .endm
+ i
+ _o\@@_:
+ .endm
+ o
+@end smallexample
+
+Produces two symbols @samp{_o0_} and @samp{_i1_}. This happens because the
+@samp{o} macro executes entirely first, putting the definition and invocation
+of the @samp{i} macro into the input buffer. It also puts the definition of
+the @samp{_o\@@_} symbol into the input buffer, evaluating the @samp{\@@}
+counter in the process and so generating a symbol called @samp{_o0_}.
+
+That finishes the invocation of @samp{o} so the @samp{\@@} counter is
+incremented. Then the input buffer is re-evaluated and the definition and
+invocation of macro @samp{i} is found. This results in @samp{_i\@@_} being put
+into the input buffer and this time @samp{\@@} evaluates to 1, so the symbol
+created is @samp{_i1_}.
+
@cindex number of times a macro has been executed
@cindex macro, execution count
@item \+
@@ -6681,6 +6736,9 @@ those explicitly specified with @code{.eject}.
Undefine the macro @var{name}, so that later uses of the string will not be
expanded. @xref{Macro}.
+Note - nested macros are automatically purged at the end of the macro that
+defines them.
+
@ifset ELF
@node PushSection
@section @code{.pushsection @var{name} [, @var{subsection}] [, "@var{flags}"[, @@@var{type}[,@var{arguments}]]]}
diff --git a/gas/input-scrub.c b/gas/input-scrub.c
index 878edc8fd36..25fac879a56 100644
--- a/gas/input-scrub.c
+++ b/gas/input-scrub.c
@@ -23,6 +23,7 @@
#include "input-file.h"
#include "sb.h"
#include "listing.h"
+#include "macro.h"
/*
* O/S independent module to supply buffers of sanitised source code
@@ -290,12 +291,13 @@ input_scrub_include_sb (sb *from, char *position, enum expansion expansion)
++macro_nest;
}
-#ifdef md_macro_start
if (expansion == expanding_macro)
{
+#ifdef md_macro_start
md_macro_start ();
- }
#endif
+ increment_macro_nesting_depth ();
+ }
next_saved_file = input_scrub_push (position);
@@ -350,6 +352,7 @@ input_scrub_next_buffer (char **bufp)
data. */
md_macro_end ();
#endif
+ decrement_macro_nesting_depth ();
}
if (from_sb_expansion != expanding_app)
--macro_nest;
diff --git a/gas/macro.c b/gas/macro.c
index 8b376f7f490..a0f2a5c764c 100644
--- a/gas/macro.c
+++ b/gas/macro.c
@@ -44,11 +44,24 @@
/* The macro hash table. */
-htab_t macro_hash;
+/* Macro nesting depth. Similar to macro_nest defined in sb.c, but this
+ counter is specific to macros, whereas macro_nest also counts repeated
+ string blocks. */
+static unsigned int macro_nesting_depth;
-/* Whether any macros have been defined. */
+/* Maximum nesting depth. Ideally the same as the value of max_macro_nest
+ as defined in as.c (ie 100). But there is one test in the assembler
+ testsuite (bfin/allinsn16.s) that nests macros to a depth of 8192. So
+ we have a ridiculously large number here. */
+#define MAX_MACRO_DEPTH 8193
-int macro_defined;
+static htab_t macro_hash[MAX_MACRO_DEPTH];
+
+/* Whether any macros have been defined.
+ FIXME: This could be a counter that is incremented
+ with .macro and decremented with .purgem. */
+
+static bool macros_defined = false;
/* Whether we should strip '@' characters. */
@@ -60,6 +73,18 @@ static unsigned int macro_number;
static void free_macro (macro_entry *);
+bool
+add_macro (macro_entry * macro, bool replace)
+{
+ if (str_hash_insert (macro_hash [macro_nesting_depth],
+ macro->name, macro, replace) == NULL)
+ {
+ macros_defined = true;
+ return true;
+ }
+ return false;
+}
+
static void
macro_del_f (void *ent)
{
@@ -72,15 +97,23 @@ macro_del_f (void *ent)
void
macro_init (void)
{
- macro_hash = htab_create_alloc (16, hash_string_tuple, eq_string_tuple,
- macro_del_f, notes_calloc, NULL);
- macro_defined = 0;
+ int i;
+
+ for (i = 0; i < MAX_MACRO_DEPTH; i++)
+ macro_hash[i] = htab_create_alloc (16, hash_string_tuple, eq_string_tuple,
+ macro_del_f, notes_calloc, NULL);
+ macros_defined = false;
}
void
macro_end (void)
{
- htab_delete (macro_hash);
+ int i;
+
+ for (i = MAX_MACRO_DEPTH; i--;)
+ htab_delete (macro_hash[i]);
+
+ macros_defined = false;
}
/* Read input lines till we get to a TO string.
@@ -655,6 +688,13 @@ free_macro (macro_entry *macro)
free (macro);
}
+static macro_entry * last_recorded_macro = NULL;
+void
+macro_record_invocation (macro_entry * macro)
+{
+ last_recorded_macro = macro;
+}
+
/* Define a new macro. */
macro_entry *
@@ -718,15 +758,19 @@ define_macro (sb *in, sb *label, size_t (*get_line) (sb *))
/* And stick it in the macro hash table. */
for (idx = 0; idx < name.len; idx++)
name.ptr[idx] = TOLOWER (name.ptr[idx]);
+
+ if (macro_nesting_depth > 0)
+ macro->parent = last_recorded_macro;
+ else
+ macro->parent = NULL;
+
if (!error)
{
- if (str_hash_insert (macro_hash, macro->name, macro, 0) != NULL)
+ if (! add_macro (macro, false))
error = _("Macro `%s' was already defined");
}
- if (!error)
- macro_defined = 1;
- else
+ if (error != NULL)
{
as_bad_where (macro->file, macro->line, error, macro->name);
free_macro (macro);
@@ -750,11 +794,25 @@ get_apost_token (size_t idx, sb *in, sb *name, int kind)
return idx;
}
-/* Substitute the actual value for a formal parameter. */
+static const char *
+macro_expand_body (sb *, sb *, formal_entry *, struct htab *,
+ const macro_entry *, unsigned int);
+
+/* Find the actual value for a formal parameter starting at START inside IN.
+ Appends the value of parameter onto OUT.
+ The hash table of formal parameters is provided by FORMAL_HASH.
+ The character that indicated the presense of a formal parameter is passed
+ in KIND.
+ If COPYIFNOTTHERE is true and the parameter is not found in the hash table
+ then it is appended as plain text onto OUT.
+ The macro containing the formal parameters is passed in MACRO.
+ This can be empty.
+ Returns the offset inside IN after advanceing past the parameter.
+ Also stores the parameter's name into T. */
static size_t
sub_actual (size_t start, sb *in, sb *t, struct htab *formal_hash,
- int kind, sb *out, int copyifnotthere)
+ int kind, sb *out, int copyifnotthere, const macro_entry * macro)
{
size_t src;
formal_entry *ptr;
@@ -768,16 +826,12 @@ sub_actual (size_t start, sb *in, sb *t, struct htab *formal_hash,
ptr = NULL;
else
ptr = str_hash_find (formal_hash, sb_terminate (t));
+
if (ptr)
{
- if (ptr->actual.len)
- {
- sb_add_sb (out, &ptr->actual);
- }
- else
- {
- sb_add_sb (out, &ptr->def);
- }
+ sb * add = ptr->actual.len ? &ptr->actual : &ptr->def;
+
+ sb_add_sb (out, add);
}
else if (kind == '&')
{
@@ -791,6 +845,55 @@ sub_actual (size_t start, sb *in, sb *t, struct htab *formal_hash,
{
sb_add_sb (out, t);
}
+ else if (!macro_strip_at
+ && macro_nesting_depth > 0
+ && macro != NULL
+ && macro->parent != NULL)
+ {
+ const macro_entry * orig_macro = macro;
+ bool success = false;
+
+ /* We have failed to find T, but we are inside nested macros. So check
+ the parent macros so see if they have a FORMAL that matches T. */
+ while (macro->parent != NULL)
+ {
+ macro = macro->parent;
+
+ ptr = str_hash_find (macro->formal_hash, t->ptr);
+ if (ptr == NULL)
+ continue;
+
+ sb * add = ptr->actual.len ? &ptr->actual : &ptr->def;
+
+ /* The parent's FORMALs might contain parameters that need further
+ substitution. See gas/testsuite/gas/arm/macro-vld1.s for an
+ example of this. */
+ if (strchr (add->ptr, '\\'))
+ {
+ sb newadd;
+
+ sb_new (&newadd);
+ /* FIXME: Should we do something if the call to
+ macro_expand_body returns an error message ? */
+ (void) macro_expand_body (add, &newadd, NULL, NULL,
+ orig_macro, orig_macro->count);
+ sb_add_sb (out, &newadd);
+ }
+ else
+ {
+ sb_add_sb (out, add);
+ }
+ success = true;
+ break;
+ }
+ if (! success)
+ {
+ /* We reached the outermost macro and failed to find T, so
+ just copy the entire parameter as is. */
+ sb_add_char (out, '\\');
+ sb_add_sb (out, t);
+ }
+ }
else
{
sb_add_char (out, '\\');
@@ -799,7 +902,12 @@ sub_actual (size_t start, sb *in, sb *t, struct htab *formal_hash,
return src;
}
-/* Expand the body of a macro. */
+/* Expands the body of a macro / block of text IN, copying it into OUT.
+ Parameters for substitution are found in FORMALS and FORMAL_HASH or
+ MACRO.
+ The number of times that this macro / block of text have already been
+ copied into the output is held in INSTANCE.
+ Returns NULL upon success or an error message otherwise. */
static const char *
macro_expand_body (sb *in, sb *out, formal_entry *formals,
@@ -811,18 +919,38 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
int inquote = 0, macro_line = 0;
formal_entry *loclist = NULL;
const char *err = NULL;
+ int nesting = 0;
+ if (formals == NULL && macro != NULL)
+ formals = macro->formals;
+
+ if (formal_hash == NULL && macro != NULL)
+ formal_hash = macro->formal_hash;
+
sb_new (&t);
while (src < in->len && !err)
{
+ if (in->ptr[src] == '.')
+ {
+ /* Check to see if we have encountered ".macro" or ".endm" */
+ if (in->len > src + 5
+ && strncmp (in->ptr + src, ".macro", 6) == 0)
+ ++ nesting;
+
+ else if (in->len > src + 4
+ && strncmp (in->ptr + src, ".endm", 5) == 0)
+ -- nesting;
+ }
+
if (in->ptr[src] == '&')
{
sb_reset (&t);
if (flag_mri)
{
if (src + 1 < in->len && in->ptr[src + 1] == '&')
- src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1);
+ src = sub_actual (src + 2, in, &t, formal_hash,
+ '\'', out, 1, macro);
else
sb_add_char (out, in->ptr[src++]);
}
@@ -830,7 +958,8 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
{
/* Permit macro parameter substitution delineated with
an '&' prefix and optional '&' suffix. */
- src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0);
+ src = sub_actual (src + 1, in, &t, formal_hash,
+ '&', out, 0, macro);
}
}
else if (in->ptr[src] == '\\')
@@ -851,7 +980,12 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
else
as_bad_where (macro->file, macro->line + macro_line, _("missing `)'"));
}
- else if (src < in->len && in->ptr[src] == '@')
+ else if (src < in->len
+ && in->ptr[src] == '@'
+ /* PR 32391: Do not perform the substition inside nested
+ macros. Instead wait until they are re-evaluated and
+ perform the substition then. */
+ && ! nesting)
{
/* Sub in the total macro invocation number. */
@@ -860,7 +994,12 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
sprintf (buffer, "%u", macro_number);
sb_add_string (out, buffer);
}
- else if (src < in->len && in->ptr[src] == '+')
+ else if (src < in->len
+ && in->ptr[src] == '+'
+ /* PR 32391: Do not perform the substition inside nested
+ macros. Instead wait until they are re-evaluated and
+ perform the substition then. */
+ && ! nesting)
{
/* Sub in the current macro invocation number. */
@@ -904,7 +1043,18 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
else
{
sb_reset (&t);
- src = sub_actual (src, in, &t, formal_hash, '\'', out, 0);
+
+ if (nesting)
+ {
+ src = get_apost_token (src, in, &t, '\'');
+ sb_add_char (out, '\\');
+ sb_add_sb (out, &t);
+ }
+ else
+ {
+ src = sub_actual (src, in, &t, formal_hash,
+ '\'', out, 0, macro);
+ }
}
}
else if ((flag_macro_alternate || flag_mri)
@@ -923,7 +1073,7 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
sb_reset (&t);
src = sub_actual (src, in, &t, formal_hash,
(macro_strip_at && inquote) ? '@' : '\'',
- out, 1);
+ out, 1, macro);
}
else
{
@@ -1035,6 +1185,7 @@ macro_expand_body (sb *in, sb *out, formal_entry *formals,
if (!err && (out->len == 0 || out->ptr[out->len - 1] != '\n'))
sb_add_char (out, '\n');
+
return err;
}
@@ -1219,8 +1370,7 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
}
}
- err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m,
- m->count);
+ err = macro_expand_body (&m->sub, out, NULL, NULL, m, m->count);
}
/* Discard any unnamed formal arguments. */
@@ -1253,9 +1403,9 @@ macro_expand (size_t idx, sb *in, macro_entry *m, sb *out)
}
/* Check for a macro. If one is found, put the expansion into
- *EXPAND. Return 1 if a macro is found, 0 otherwise. */
+ *EXPAND. Return TRUE if a macro is found, FALSE otherwise. */
-int
+bool
check_macro (const char *line, sb *expand,
const char **error, macro_entry **info)
{
@@ -1264,9 +1414,12 @@ check_macro (const char *line, sb *expand,
macro_entry *macro;
sb line_sb;
+ if (! macros_defined)
+ return false;
+
if (! is_name_beginner (*line)
&& (! flag_mri || *line != '.'))
- return 0;
+ return false;
s = line + 1;
while (is_part_of_name (*s))
@@ -1278,11 +1431,17 @@ check_macro (const char *line, sb *expand,
for (cls = copy; *cls != '\0'; cls ++)
*cls = TOLOWER (*cls);
- macro = str_hash_find (macro_hash, copy);
+ int i;
+ for (i = macro_nesting_depth; i >= 0; i--)
+ {
+ macro = str_hash_find (macro_hash[i], copy);
+ if (macro != NULL)
+ break;
+ }
free (copy);
if (macro == NULL)
- return 0;
+ return false;
/* Wrap the line up in an sb. */
sb_new (&line_sb);
@@ -1298,7 +1457,7 @@ check_macro (const char *line, sb *expand,
if (info)
*info = macro;
- return 1;
+ return true;
}
/* Delete a macro. */
@@ -1316,11 +1475,20 @@ delete_macro (const char *name)
copy[i] = TOLOWER (name[i]);
copy[i] = '\0';
- macro = str_hash_find (macro_hash, copy);
- if (macro != NULL)
- str_hash_delete (macro_hash, copy);
- else
+ int j;
+ for (j = macro_nesting_depth; j >= 0; j--)
+ {
+ macro = str_hash_find (macro_hash [j], copy);
+ if (macro != NULL)
+ {
+ str_hash_delete (macro_hash[j], copy);
+ break;
+ }
+ }
+
+ if (macro == NULL)
as_warn (_("Attempt to purge non-existing macro `%s'"), copy);
+
free (copy);
}
@@ -1422,3 +1590,25 @@ expand_irp (int irpc, size_t idx, sb *in, sb *out, size_t (*get_line) (sb *))
return err;
}
+
+void
+increment_macro_nesting_depth (void)
+{
+ if (macro_nesting_depth >= (MAX_MACRO_DEPTH - 1))
+ as_fatal (_("macros nested too deeply"));
+ else
+ ++macro_nesting_depth;
+}
+
+void
+decrement_macro_nesting_depth (void)
+{
+ if (macro_nesting_depth == 0)
+ as_fatal (_("too much macro un-nesting"));
+ else
+ {
+ /* FIXME: Potential memory leak here. */
+ htab_empty (macro_hash [macro_nesting_depth]);
+ --macro_nesting_depth;
+ }
+}
diff --git a/gas/macro.h b/gas/macro.h
index e87f64e70ca..97f2a5a4066 100644
--- a/gas/macro.h
+++ b/gas/macro.h
@@ -64,31 +64,28 @@ typedef struct macro_struct
int formal_count; /* Number of formal args. */
formal_entry * formals; /* List of formal_structs. */
htab_t formal_hash; /* Hash table of formals. */
+ struct macro_struct * parent; /* Parent of nested macros. */
const char * name; /* Macro name. */
const char * file; /* File the macro was defined in. */
unsigned int line; /* Line number of definition. */
unsigned int count; /* Invocation count. */
} macro_entry;
-/* Whether any macros have been defined. */
-
-extern int macro_defined;
-
-/* The macro nesting level. */
+/* The macro/text block nesting level. */
extern int macro_nest;
-/* The macro hash table. */
-
-extern htab_t macro_hash;
-
extern int buffer_and_nest (const char *, const char *, sb *,
size_t (*) (sb *));
extern void macro_init (void);
extern void macro_end (void);
extern macro_entry *define_macro (sb *, sb *, size_t (*) (sb *));
-extern int check_macro (const char *, sb *, const char **, macro_entry **);
+extern bool check_macro (const char *, sb *, const char **, macro_entry **);
extern void delete_macro (const char *);
extern const char *expand_irp (int, size_t, sb *, sb *, size_t (*) (sb *));
+extern void increment_macro_nesting_depth (void);
+extern void decrement_macro_nesting_depth (void);
+extern void macro_record_invocation (macro_entry *);
+extern bool add_macro (macro_entry *, bool);
#endif
diff --git a/gas/read.c b/gas/read.c
index 589c7b080c2..e5185f4ba26 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -655,7 +655,8 @@ poend (void)
}
/* Helper function of read_a_source_file, which tries to expand a macro. */
-static int
+
+static bool
try_macro (char term, const char *line)
{
sb out;
@@ -672,12 +673,14 @@ try_macro (char term, const char *line)
sb_kill (&out);
buffer_limit =
input_scrub_next_buffer (&input_line_pointer);
+
+ macro_record_invocation (macro);
#ifdef md_macro_info
md_macro_info (macro);
#endif
- return 1;
+ return true;
}
- return 0;
+ return false;
}
#ifdef HANDLE_BUNDLE
@@ -1269,7 +1272,7 @@ read_a_source_file (const char *name)
s_ignore (0);
nul_char = next_char = *--input_line_pointer;
*input_line_pointer = '\0';
- if (! macro_defined || ! try_macro (next_char, s))
+ if (! try_macro (next_char, s))
{
*end = '\0';
as_bad (_("unknown pseudo-op: `%s'"), s);
@@ -1306,7 +1309,7 @@ read_a_source_file (const char *name)
generate_lineno_debug ();
- if (macro_defined && try_macro (next_char, s))
+ if (try_macro (next_char, s))
continue;
if (mri_pending_align)
@@ -2816,7 +2819,7 @@ s_macro (int ignore ATTRIBUTE_UNUSED)
as_warn_where (macro->file, macro->line,
_("attempt to redefine pseudo-op `%s' ignored"),
macro->name);
- str_hash_delete (macro_hash, macro->name);
+ delete_macro (macro->name);
}
}
diff --git a/gas/testsuite/gas/macros/macros.exp b/gas/testsuite/gas/macros/macros.exp
index 3ac199feaa5..94ac37cdb8b 100644
--- a/gas/testsuite/gas/macros/macros.exp
+++ b/gas/testsuite/gas/macros/macros.exp
@@ -112,3 +112,4 @@ run_list_test count
run_list_test irp-count
run_list_test irpc-quote
run_list_test rept-count
+run_dump_test nesting
diff --git a/gas/testsuite/gas/macros/nesting.d b/gas/testsuite/gas/macros/nesting.d
new file mode 100644
index 00000000000..2f44aedb2dc
--- /dev/null
+++ b/gas/testsuite/gas/macros/nesting.d
@@ -0,0 +1,28 @@
+#nm: -j
+#name: Nested macros (PR 32391)
+# Sone targets do not support macros used like this.
+#skip: tic*-*-* mmix-*
+
+#...
+_m7_
+_m8_
+after_at_0
+after_at_3
+after_plus_0
+after_plus_1
+before_at_0
+before_at_3
+before_plus_0
+before_plus_1
+bert
+harryfred
+i3_bar
+inside_at_1
+inside_at_2
+inside_at_4
+inside_at_5
+inside_plus_0
+inside_plus_1
+jim
+o3_foo
+other_inner_6
diff --git a/gas/testsuite/gas/macros/nesting.s b/gas/testsuite/gas/macros/nesting.s
new file mode 100644
index 00000000000..438d5af8d00
--- /dev/null
+++ b/gas/testsuite/gas/macros/nesting.s
@@ -0,0 +1,104 @@
+
+ .text
+/* PR 32391: Automatic counters inside macros should increment when nested
+ macros finish execution. */
+.macro o1
+.global before_at_\@
+before_at_\@:
+.global before_plus_\+
+before_plus_\+:
+
+ .macro i1
+.global inside_at_\@
+inside_at_\@:
+.global inside_plus_\+
+inside_plus_\+:
+ .endm
+
+ i1
+ i1
+
+.global after_at_\@
+after_at_\@:
+.global after_plus_\+
+after_plus_\+:
+
+.endm
+
+/* Invoking o1 should produce these symbols in this order:
+
+ before_at_0
+ before_plus_0
+ inside_at_1
+ inside_plus_0
+ inside_at_2
+ inside_plus_1
+ after_at_0
+ after_plus_0 */
+o1
+
+/* A second invocation of o1 should not produce any errors about
+ symbols or macros being redefined. */
+o1
+
+/* This definition should not collide with the definition inside o1. */
+.macro i1
+.global other_inner_\@
+other_inner_\@:
+.endm
+
+/* And invoking it should invoke the second defintion of i1, not the first. */
+i1
+
+.macro o2
+.global _m\@_
+_m\@_:
+.macro i2
+.global _m\@_
+_m\@_:
+.endm
+i2
+.endm
+
+/* This should not generate conflicting symbols because the assembler
+ inserts the contents of o2 into the input buffer as pure text (ie
+ without evaluating i2). The first use of \@ is evaluated at this
+ time, creating _m4_. But the second use is not evaluated because
+ it is inside a .macro definition.
+
+ This finishes the evaluation of o2, so the \@ counter is incremented.
+
+ Next the input buffer is re-evaluated and the i2 macro definition
+ and invocation are encounterd. The text from i2 are inserted into
+ the input buffer and at this point the second use of \@ is evaluated
+ resulting in the creation of a symbol called _m5_. */
+o2
+
+/* Macro arguments should be independent of nesting. */
+.macro O3 arg
+.global o3_\arg
+o3_\arg:
+
+ .macro I3 arg
+.global i3_\arg
+i3_\arg:
+ .endm
+
+ i3 bar /* Macro names are case insensitive. */
+.endm
+
+o3 foo /* Should produce two labels: o3_foo and i3_bar. */
+
+/* Nested macros can access the arguments of their parents.
+ In addition their arguments can be substituted into the arguments
+ that are substited from their parents: */
+.macro OUTER arg1, arg2, arg3:vararg
+ .macro INNER arg4 arg2
+ .dc.a \arg2
+ .dc.a \arg3
+ .endm
+ INNER \arg1 bert
+ .dc.a \arg2
+.endm
+
+OUTER fred, jim, harry\arg4 /* This produces references to "jim", "bert" and "harryfred". */
More information about the Binutils-cvs
mailing list