This now also addresses the inconsistent acceptance of identifiers between the macro handling code and the rest of the assembler, including the unability to use a macro the name of which starts with a dot. Built and tested natively on ia64-unknown-linux-gnu and as cross tools for a large number of targets. Unfortunately, it breaks the mmix test 'relax2', and after a bit of investigation I can't see how this could be fixed (mmix considers ':' a normal symbol character, and hence constructs like \x: in a macro now - correctly - try to find "x:" among the parameters, while previously only "x" was used and the colon left alone). Jan gas/ 2005-02-04 Jan Beulich * macro.c (get_token): Use is_name_beginner/is_part_of_name/ is_name_ender. (check_macro): Likewise. (buffer_and_nest): Likewise. Permit multiple labels. Don't discard labels together with the closing pseudo-op. (macro_expand_body): Adjust comment. Range-check input before use. Adjust mis-spelled diagnostic. Use is_name_beginner. * read.c (try_macro): New. (read_a_source_file): New static variable last_eol. Don't list macro expansion lines more than once. Call try_macro. (s_macro): Set section of line_label to absolute instead of undefined. gas/testsuite/ 2005-02-04 Jan Beulich * gas/macros/dot.[ls]: New. * gas/macros/macros.exp: Run new test. --- /home/jbeulich/src/binutils/mainline/2005-02-01/gas/macro.c 2005-01-31 15:27:07.000000000 +0100 +++ 2005-02-01/gas/macro.c 2005-02-03 15:12:04.000000000 +0100 @@ -187,21 +187,37 @@ buffer_and_nest (const char *from, const the first column, since we can't tell what's a label and whats a pseudoop. */ - /* Skip leading whitespace. */ - while (i < ptr->len && ISWHITE (ptr->ptr[i])) - i++; - - /* Skip over a label. */ - while (i < ptr->len - && (ISALNUM (ptr->ptr[i]) - || ptr->ptr[i] == '_' - || ptr->ptr[i] == '$')) - i++; - - /* And a colon. */ - if (i < ptr->len - && ptr->ptr[i] == ':') - i++; + if (! LABELS_WITHOUT_COLONS) + { + /* Skip leading whitespace. */ + while (i < ptr->len && ISWHITE (ptr->ptr[i])) + i++; + } + + 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++; + if (LABELS_WITHOUT_COLONS) + 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; + } } /* Skip trailing whitespace. */ @@ -226,11 +242,13 @@ buffer_and_nest (const char *from, const ? strncasecmp (ptr->ptr + i, from, from_len) == 0 : from_len > 0) && (ptr->len == (i + from_len) - || ! ISALNUM (ptr->ptr[i + 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) - || ! ISALNUM (ptr->ptr[i + to_len]))) + || ! (is_part_of_name (ptr->ptr[i + to_len]) + || is_name_ender (ptr->ptr[i + to_len])))) { depth--; if (depth == 0) @@ -258,15 +276,16 @@ static int get_token (int idx, sb *in, sb *name) { if (idx < in->len - && (ISALPHA (in->ptr[idx]) - || in->ptr[idx] == '_' - || in->ptr[idx] == '$')) + && is_name_beginner (in->ptr[idx])) { sb_add_char (name, in->ptr[idx++]); while (idx < in->len - && (ISALNUM (in->ptr[idx]) - || in->ptr[idx] == '_' - || in->ptr[idx] == '$')) + && is_part_of_name (in->ptr[idx])) + { + sb_add_char (name, in->ptr[idx++]); + } + if (idx < in->len + && is_name_ender (in->ptr[idx])) { sb_add_char (name, in->ptr[idx++]); } @@ -692,13 +711,14 @@ macro_expand_body (sb *in, sb *out, form else { /* FIXME: Why do we do this? */ + /* At least in alternate mode this seems correct. */ src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0); } } else if (in->ptr[src] == '\\') { src++; - if (in->ptr[src] == '(') + if (src < in->len && in->ptr[src] == '(') { /* Sub in till the next ')' literally. */ src++; @@ -709,9 +729,9 @@ macro_expand_body (sb *in, sb *out, form if (in->ptr[src] == ')') src++; else - return _("missplaced )"); + return _("misplaced `)'"); } - else if (in->ptr[src] == '@') + else if (src < in->len && in->ptr[src] == '@') { /* Sub in the macro invocation number. */ @@ -720,7 +740,7 @@ macro_expand_body (sb *in, sb *out, form sprintf (buffer, "%d", macro_number); sb_add_string (out, buffer); } - else if (in->ptr[src] == '&') + else if (src < in->len && in->ptr[src] == '&') { /* This is a preprocessor variable name, we don't do them here. */ @@ -728,7 +748,7 @@ macro_expand_body (sb *in, sb *out, form sb_add_char (out, '&'); src++; } - else if (macro_mri && ISALNUM (in->ptr[src])) + else if (macro_mri && src < in->len && ISALNUM (in->ptr[src])) { int ind; formal_entry *f; @@ -759,9 +779,7 @@ macro_expand_body (sb *in, sb *out, form } } else if ((macro_alternate || macro_mri) - && (ISALPHA (in->ptr[src]) - || in->ptr[src] == '_' - || in->ptr[src] == '$') + && is_name_beginner (in->ptr[src]) && (! inquote || ! macro_strip_at || (src > 0 && in->ptr[src - 1] == '@'))) @@ -1086,16 +1104,14 @@ check_macro (const char *line, sb *expan macro_entry *macro; sb line_sb; - if (! ISALPHA (*line) - && *line != '_' - && *line != '$' + if (! is_name_beginner (*line) && (! macro_mri || *line != '.')) return 0; s = line + 1; - while (ISALNUM (*s) - || *s == '_' - || *s == '$') + while (is_part_of_name (*s)) + ++s; + if (is_name_ender (*s)) ++s; copy = (char *) alloca (s - line + 1); --- /home/jbeulich/src/binutils/mainline/2005-02-01/gas/read.c 2005-01-26 08:33:13.000000000 +0100 +++ 2005-02-01/gas/read.c 2005-02-03 15:12:04.000000000 +0100 @@ -499,6 +499,32 @@ scrub_from_string (char *buf, int buflen return copy; } +/* Helper function of read_a_source_file, which tries to expand a macro. */ +static int +try_macro (char term, const char *line) +{ + sb out; + const char *err; + macro_entry *macro; + + if (check_macro (line, &out, &err, ¯o)) + { + if (err != NULL) + as_bad ("%s", err); + *input_line_pointer++ = term; + input_scrub_include_sb (&out, + input_line_pointer, 1); + sb_kill (&out); + buffer_limit = + input_scrub_next_buffer (&input_line_pointer); +#ifdef md_macro_info + md_macro_info (macro); +#endif + return 1; + } + return 0; +} + /* We read the file, putting things into a web that represents what we have been reading. */ void @@ -526,6 +552,13 @@ read_a_source_file (char *name) while ((buffer_limit = input_scrub_next_buffer (&input_line_pointer)) != 0) { /* We have another line to parse. */ +#ifndef NO_LISTING + /* In order to avoid listing macro expansion lines with labels + multiple times, keep track of which line was last issued. */ + static char *last_eol; + + last_eol = NULL; +#endif know (buffer_limit[-1] == '\n'); /* Must have a sentinel. */ while (input_line_pointer < buffer_limit) @@ -645,17 +678,21 @@ read_a_source_file (char *name) if (is_end_of_line[(unsigned char) *s]) break; - /* Copy it for safe keeping. Also give an indication of - how much macro nesting is involved at this point. */ - len = s - (input_line_pointer - 1); - copy = (char *) xmalloc (len + macro_nest + 2); - memset (copy, '>', macro_nest); - copy[macro_nest] = ' '; - memcpy (copy + macro_nest + 1, input_line_pointer - 1, len); - copy[macro_nest + 1 + len] = '\0'; + if (s != last_eol) + { + last_eol = s; + /* Copy it for safe keeping. Also give an indication of + how much macro nesting is involved at this point. */ + len = s - (input_line_pointer - 1); + copy = (char *) xmalloc (len + macro_nest + 2); + memset (copy, '>', macro_nest); + copy[macro_nest] = ' '; + memcpy (copy + macro_nest + 1, input_line_pointer - 1, len); + copy[macro_nest + 1 + len] = '\0'; - /* Install the line with the listing facility. */ - listing_newline (copy); + /* Install the line with the listing facility. */ + listing_newline (copy); + } } else listing_newline (NULL); @@ -795,9 +832,17 @@ read_a_source_file (char *name) /* Print the error msg now, while we still can. */ if (pop == NULL) { - as_bad (_("unknown pseudo-op: `%s'"), s); + char *end = input_line_pointer; + *input_line_pointer = c; s_ignore (0); + c = *input_line_pointer; + *input_line_pointer = '\0'; + if (! macro_defined || ! try_macro (c, s)) + { + *end = '\0'; + as_bad (_("unknown pseudo-op: `%s'"), s); + } continue; } @@ -853,28 +898,8 @@ read_a_source_file (char *name) generate_lineno_debug (); - if (macro_defined) - { - sb out; - const char *err; - macro_entry *macro; - - if (check_macro (s, &out, &err, ¯o)) - { - if (err != NULL) - as_bad ("%s", err); - *input_line_pointer++ = c; - input_scrub_include_sb (&out, - input_line_pointer, 1); - sb_kill (&out); - buffer_limit = - input_scrub_next_buffer (&input_line_pointer); -#ifdef md_macro_info - md_macro_info (macro); -#endif - continue; - } - } + if (macro_defined && try_macro (c, s)) + continue; if (mri_pending_align) { @@ -2299,7 +2324,7 @@ s_macro (int ignore ATTRIBUTE_UNUSED) { if (line_label != NULL) { - S_SET_SEGMENT (line_label, undefined_section); + S_SET_SEGMENT (line_label, absolute_section); S_SET_VALUE (line_label, 0); symbol_set_frag (line_label, &zero_address_frag); } --- /home/jbeulich/src/binutils/mainline/2005-02-01/gas/testsuite/gas/macros/dot.l 1970-01-01 01:00:00.000000000 +0100 +++ 2005-02-01/gas/testsuite/gas/macros/dot.l 2005-02-04 10:54:39.000000000 +0100 @@ -0,0 +1,22 @@ +.*: Assembler messages: +.*:[1-9][0-9]*: Warning: attempt to redefine pseudo-op `.macro' ignored +.*:[1-9][0-9]*: Error: unknown pseudo-op: `.xyz' +.*:[1-9][0-9]*: Error: unknown pseudo-op: `.y.z' +(.* )?GAS .* +#... +[ ]*[1-9][0-9]*[ ]+m 4, 2 +[ ]*[1-9][0-9]*[ ]+> \.data +[ ]*[1-9][0-9]*[ ]+> labelA:labelB:labelC:labelD:x\.y\.z 4\+2 +[ ]*[1-9][0-9]*[ ]+>> \.align 4 +[ ]*[1-9][0-9]*[ ]+\?+[ ]+0606[ ]+>> \.byte 4\+2,4\+2 +[ ]*[1-9][0-9]*[ ]+\?+[ ]+0000[ ]+> \.skip 2 +[ ]*[1-9][0-9]*[ ]+> labelZ:labelY:labelX:labelW:\.xyz 4-2 +[ ]*[1-9][0-9]*[ ]+>> \.align 8 +[ ]*[1-9][0-9]*[ ]+\?+[ ]+0202[ ]+>> \.byte 4-2,4-2 +[ ]*[1-9][0-9]*[ ]+\?+[ ]+0000 ?0000[ ]+> \.skip 4\*2 +[ ]*[1-9][0-9]*[ ]+0000 ?0000[ ]* +[ ]*[1-9][0-9]*[ ]+> label9:label8:label7:label6: +[ ]*[1-9][0-9]*[ ]+ +[ ]*[1-9][0-9]*[ ]+\.purgem \.xyz, x\.y\.z +[ ]*[1-9][0-9]*[ ]+\.xyz 0 +[ ]*[1-9][0-9]*[ ]+x\.y\.z 0 --- /home/jbeulich/src/binutils/mainline/2005-02-01/gas/testsuite/gas/macros/dot.s 1970-01-01 01:00:00.000000000 +0100 +++ 2005-02-01/gas/testsuite/gas/macros/dot.s 2005-02-04 10:42:36.000000000 +0100 @@ -0,0 +1,28 @@ +.altmacro + +.macro x.y.z val + .align 4 + .byte &val, &val +.endm + +.macro .xyz val + .align 8 + .byte &val, &val +.endm + +.macro .macro +.endm + +label1:label2 : label3 :label4: m: .macro arg.1, arg.2 + .data +labelA:labelB : labelC :labelD: x.y.z &arg.1+&arg.2 + .skip &arg.2 +labelZ:labelY : labelX :labelW: .xyz &arg.1-&arg.2 + .skip &arg.1*&arg.2 +label9:label8 : label7 :label6: .endm + +m 4, 2 + +.purgem .xyz, x.y.z +.xyz 0 +x.y.z 0 --- /home/jbeulich/src/binutils/mainline/2005-02-01/gas/testsuite/gas/macros/macros.exp 2005-01-31 15:27:07.000000000 +0100 +++ 2005-02-01/gas/testsuite/gas/macros/macros.exp 2005-02-04 16:44:30.743421618 +0100 @@ -63,5 +63,14 @@ run_dump_test app3 run_dump_test app4 run_list_test badarg "" +case $target_triplet in { + { *c54x*-*-* } { } + { *c4x*-*-* } { } + { h8500-*-* } { } + { m68*-*-* } { } + { m88*-*-* } { } + { mmix-* } { } + default { run_list_test dot "-alm" } +} run_list_test end "" run_list_test redef ""