From: Nick Clifton Date: Thu, 19 Dec 2024 09:59:11 +0000 (+0000) Subject: Fix the handling or arguments and macro pseudo-variables inside nested assembler... X-Git-Tag: gdb-16-branchpoint~43 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9f2e3c21f65;p=thirdparty%2Fbinutils-gdb.git Fix the handling or arguments and macro pseudo-variables inside nested assembler macros. PR 32391 --- 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". */