diff options
author | H. Peter Anvin <hpa@zytor.com> | 2019-08-10 01:38:06 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2019-08-10 01:46:58 -0700 |
commit | 322bee0aac0f177127e4a33aff94a5fa2158a0f9 (patch) | |
tree | 77ea5f8db299b2ff7c32e25a5c770f9e3e9c6975 /asm | |
parent | ab6f8319552f17d269a5bf2facea48ea1c338b71 (diff) | |
download | nasm-322bee0aac0f177127e4a33aff94a5fa2158a0f9.tar.gz |
Additional listing options, improve help output, fix macro limits
Additional listing options:
-Ld to display counts in decimal
-Lp to output a list file in every pass (to make sure one exists)
Clean up the help output and make it comprehensive. The -hf and -y
options are no longer necessary, although they are supported for
backwards compatiblity.
Fix macro-levels so it actually count descent levels; a new
macro-tokens limit introduced for the actual token limit.
Slightly simplify the limits code.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'asm')
-rw-r--r-- | asm/listing.c | 49 | ||||
-rw-r--r-- | asm/nasm.c | 279 | ||||
-rw-r--r-- | asm/preproc.c | 56 |
3 files changed, 242 insertions, 142 deletions
diff --git a/asm/listing.c b/asm/listing.c index 50e63d85..9d263769 100644 --- a/asm/listing.c +++ b/asm/listing.c @@ -200,6 +200,20 @@ static void list_address(int64_t offset, const char *brackets, list_out(offset, q); } +static void list_size(int64_t offset, const char *tag, uint64_t size) +{ + char buf[64]; + const char *fmt; + + if (list_option('d')) + fmt = "<%s %"PRIu64">"; + else + fmt = "<%s %"PRIX64">"; + + snprintf(buf, sizeof buf, fmt, tag, size); + list_out(offset, buf); +} + static void list_output(const struct out_data *data) { char q[24]; @@ -214,8 +228,7 @@ static void list_output(const struct out_data *data) switch (data->type) { case OUT_ZERODATA: if (size > 16) { - snprintf(q, sizeof(q), "<zero %08"PRIX64">", size); - list_out(offset, q); + list_size(offset, "zero", size); break; } else { p = zero_buffer; @@ -223,13 +236,19 @@ static void list_output(const struct out_data *data) /* fall through */ case OUT_RAWDATA: { - if (size == 0 && !listdata[0]) - listoffset = data->offset; - while (size--) { - HEX(q, *p); - q[2] = '\0'; - list_out(offset++, q); - p++; + if (size == 0) { + if (!listdata[0]) + listoffset = data->offset; + } else if (p) { + while (size--) { + HEX(q, *p); + q[2] = '\0'; + list_out(offset++, q); + p++; + } + } else { + /* Used for listing on non-code generation passes with -Lp */ + list_size(offset, "len", size); } break; } @@ -249,8 +268,8 @@ static void list_output(const struct out_data *data) break; case OUT_RESERVE: { - snprintf(q, sizeof(q), "<res %"PRIX64">", size); - list_out(offset, q); + if (size) + list_size(offset, "res", size); break; } default: @@ -295,22 +314,18 @@ static void mistack_push(bool inhibiting) static void list_uplevel(int type, int64_t size) { - char str[64]; - if (!listp) return; switch (type) { case LIST_INCBIN: suppress |= 1; - snprintf(str, sizeof str, "<bin %"PRIX64">", size); - list_out(listoffset, str); + list_size(listoffset, "bin", size); break; case LIST_TIMES: suppress |= 2; - snprintf(str, sizeof str, "<rep %"PRIX64">", size); - list_out(listoffset, str); + list_size(listoffset, "rep", size); break; case LIST_INCLUDE: @@ -69,12 +69,14 @@ struct forwrefinfo { /* info held on forward refs. */ int operand; }; +const char *_progname; + static void parse_cmdline(int, char **, int); static void assemble_file(const char *, struct strlist *); static bool skip_this_pass(errflags severity); static void nasm_verror_asm(errflags severity, const char *fmt, va_list args); static void usage(void); -static void help(char xopt); +static void help(FILE *); struct error_format { const char *beforeline; /* Before line number, if present */ @@ -172,24 +174,38 @@ static char *(*quote_for_make)(const char *) = quote_for_pmake; * Execution limits that can be set via a command-line option or %pragma */ -#define LIMIT_MAX_VAL (INT64_MAX >> 1) /* Effectively unlimited */ +/* + * This is really unlimited; it would take far longer than the + * current age of the universe for this limit to be reached even on + * much faster CPUs than currently exist. +*/ +#define LIMIT_MAX_VAL (INT64_MAX >> 1) -int64_t nasm_limit[LIMIT_MAX+1] = -{ LIMIT_MAX_VAL, 1000, 1000000, 1000000, 1000000, 2000000000 }; +int64_t nasm_limit[LIMIT_MAX+1]; struct limit_info { const char *name; const char *help; + int64_t default_val; }; +/* The order here must match enum nasm_limit in nasm.h */ static const struct limit_info limit_info[LIMIT_MAX+1] = { - { "passes", "total number of passes" }, - { "stalled-passes", "number of passes without forward progress" }, - { "macro-levels", "levels of macro expansion"}, - { "rep", "%rep count" }, - { "eval", "expression evaluation descent"}, - { "lines", "total source lines processed"} + { "passes", "total number of passes", LIMIT_MAX_VAL }, + { "stalled-passes", "number of passes without forward progress", 1000 }, + { "macro-levels", "levels of macro expansion", 10000 }, + { "macro-tokens", "tokens processed during macro expansion", 10000000 }, + { "rep", "%rep count", 1000000 }, + { "eval", "expression evaluation descent", 1000000}, + { "lines", "total source lines processed", 2000000000 } }; +static void set_default_limits(void) +{ + int i; + for (i = 0; i <= LIMIT_MAX; i++) + nasm_limit[i] = limit_info[i].default_val; +} + enum directive_result nasm_set_limit(const char *limit, const char *valstr) { @@ -382,7 +398,7 @@ static void emit_dependencies(struct strlist *list) linepos += len+1; nasm_free(file); } - fprintf(deps, "\n\n"); + fputs("\n\n", deps); strlist_for_each(l, list) { if (depend_emit_phony) { @@ -453,6 +469,10 @@ static void timestamp(void) int main(int argc, char **argv) { + _progname = argv[0]; + if (!_progname || !_progname[0]) + _progname = "nasm"; + timestamp(); error_file = stderr; @@ -460,6 +480,8 @@ int main(int argc, char **argv) iflag_set_default_cpu(&cpu); iflag_set_default_cpu(&cmd_cpu); + set_default_limits(); + include_path = strlist_alloc(true); _pass_type = PASS_INIT; @@ -998,6 +1020,10 @@ static bool process_arg(char *p, char *q, int pass) list_options |= (UINT64_C(1) << p); param++; } + if (list_options & (UINT64_C(1) << ('p' - '@'))) { + /* Do listings for every pass */ + active_list_options = list_options; + } } break; @@ -1033,14 +1059,13 @@ static bool process_arg(char *p, char *q, int pass) break; case 'h': - help(p[2]); + help(stdout); exit(0); /* never need usage message here */ break; case 'y': - printf("\nvalid debug formats for '%s' output format are" - " ('*' denotes default):\n", ofmt->shortname); - dfmt_list(ofmt, stdout); + /* legacy option */ + dfmt_list(stdout); exit(0); break; @@ -1238,7 +1263,7 @@ static bool process_arg(char *p, char *q, int pass) debug_nasm = param ? strtoul(param, NULL, 10) : debug_nasm+1; break; case OPT_HELP: - help(0); + help(stdout); exit(0); default: panic(); @@ -1495,7 +1520,20 @@ static void process_insn(insn *instruction) for (n = 1; n <= instruction->times; n++) { l = insn_size(location.segment, location.offset, globalbits, instruction); - if (l != -1) /* l == -1 -> invalid instruction */ + + if (list_option('p')) { + if (l > 0) { + struct out_data dummy; + memset(&dummy, 0, sizeof dummy); + dummy.type = OUT_RESERVE; + dummy.offset = location.offset; + dummy.size = l; + lfmt->output(&dummy); + } + } + + /* l == -1 -> invalid instruction */ + if (l != -1) increment_offset(l); } } else { @@ -1566,7 +1604,7 @@ static void assemble_file(const char *fname, struct strlist *depend_list) globalbits = cmd_sb; /* set 'bits' to command line default */ cpu = cmd_cpu; - if (pass_final()) { + if (pass_final() || list_option('p')) { active_list_options = list_options; lfmt->init(listname); } @@ -1933,95 +1971,136 @@ static void usage(void) fputs("type `nasm -h' for help\n", error_file); } -static void help(const char xopt) +static void help(FILE *out) { int i; - printf - ("usage: nasm [-@ response file] [-o outfile] [-f format] " - "[-l listfile]\n" - " [options...] [--] filename\n" - " or nasm -v (or --v) for version info\n\n" - "\n" - "Response files should contain command line parameters,\n" - "one per line.\n" - "\n" - " -t assemble in SciTech TASM compatible mode\n"); - printf - (" -E (or -e) preprocess only (writes output to stdout by default)\n" - " -a don't preprocess (assemble only)\n" - " -M generate Makefile dependencies on stdout\n" - " -MG d:o, missing files assumed generated\n" - " -MF file set Makefile dependency file\n" - " -MD file assemble and generate dependencies\n" - " -MT file dependency target name\n" - " -MQ file dependency target name (quoted)\n" - " -MP emit phony target\n\n" - " -Zfile redirect error messages to file\n" - " -s redirect error messages to stdout\n\n" - " -g generate debugging information\n\n" - " -F format select a debugging format\n\n" - " -gformat same as -g -F format\n\n" - " -o outfile write output to an outfile\n\n" - " -f format select an output format\n\n" - " -l listfile write listing to a list file\n" - " -Lflags... add optional information to the list file\n" - " -Le show the preprocessed output\n\n" - " -Lm show all single-line macro definitions\n\n" - " -Ipath add a pathname to the include file path\n"); - printf - (" -Oflags... optimize opcodes, immediates and branch offsets\n" - " -O0 no optimization\n" - " -O1 minimal optimization\n" - " -Ox multipass optimization (default)\n" - " -Ov display the number of passes executed at the end\n" - " -Pfile pre-include a file (also --include)\n" - " -Dmacro[=str] pre-define a macro\n" - " -Umacro undefine a macro\n" - " -Xformat specifiy error reporting format (gnu or vc)\n" - " -w+foo enable warning foo (equiv. -Wfoo)\n" - " -w-foo disable warning foo (equiv. -Wno-foo)\n" - " -w[+-]error[=foo]\n" - " promote [specific] warnings to errors\n" - " -h show invocation summary and exit (also --help)\n\n" - " --pragma str pre-executes a specific %%pragma\n" - " --before str add line (usually a preprocessor statement) before the input\n" - " --prefix str prepend the given string to all the given string\n" - " to all extern, common and global symbols (also --gprefix)\n" - " --postfix str append the given string to all the given string\n" - " to all extern, common and global symbols (also --gpostfix)\n" - " --lprefix str prepend the given string to all other symbols\n" - " --lpostfix str append the given string to all other symbols\n" - " --keep-all output files will not be removed even if an error happens\n" - " --no-line ignore %%line directives in input\n" - " --limit-X val set execution limit X\n"); - - for (i = 0; i <= LIMIT_MAX; i++) { - printf(" %-15s %s (default ", - limit_info[i].name, limit_info[i].help); - if (nasm_limit[i] < LIMIT_MAX_VAL) { - printf("%"PRId64")\n", nasm_limit[i]); - } else { - printf("unlimited)\n"); + fprintf(out, + "Usage: %s [-@ response file] [options...] [--] filename\n" + " %s -v (or --v)\n" + "\n", + _progname, _progname); + fputs( + "\n" + "Response files should contain command line parameters,\n" + "one per line.\n" + "\n" + "Values in brackets indicate defaults\n" + "\n" + " -h show this text and exit (also --help)\n" + " -v print the NASM version number and exit\n" + "\n" + " -o outfile write output to outfile\n" + " --keep-all output files will not be removed even if an error happens\n" + "\n" + " -Xformat specifiy error reporting format (gnu or vc)\n" + " -s redirect error messages to stdout\n" + " -Zfile redirect error messages to file\n" + "\n" + " -M generate Makefile dependencies on stdout\n" + " -MG d:o, missing files assumed generated\n" + " -MF file set Makefile dependency file\n" + " -MD file assemble and generate dependencies\n" + " -MT file dependency target name\n" + " -MQ file dependency target name (quoted)\n" + " -MP emit phony targets\n" + "\n" + " -f format select output file format\n" + , out); + ofmt_list(ofmt, out); + fputs( + "\n" + " -g generate debugging information\n" + " -F format select a debugging format (output format dependent)\n" + " -gformat same as -g -F format\n" + , out); + dfmt_list(out); + fputs( + "\n" + " -l listfile write listing to a list file\n" + " -Lflags... add optional information to the list file\n" + " -Ld show byte and repeat counts in decimal, not hex\n" + " -Le show the preprocessed output\n" + " -Lm show all single-line macro definitions\n" + " -Lp output a list file every pass, in case of errors\n" + "\n" + " -Oflags... optimize opcodes, immediates and branch offsets\n" + " -O0 no optimization\n" + " -O1 minimal optimization\n" + " -Ox multipass optimization (default)\n" + " -Ov display the number of passes executed at the end\n" + " -t assemble in limited SciTech TASM compatible mode\n" + "\n" + " -E (or -e) preprocess only (writes output to stdout by default)\n" + " -a don't preprocess (assemble only)\n" + " -Ipath add a pathname to the include file path\n" + " -Pfile pre-include a file (also --include)\n" + " -Dmacro[=str] pre-define a macro\n" + " -Umacro undefine a macro\n" + " --pragma str pre-executes a specific %%pragma\n" + " --before str add line (usually a preprocessor statement) before the input\n" + " --no-line ignore %line directives in input\n" + "\n" + " --prefix str prepend the given string to the names of all extern,\n" + " common and global symbols (also --gprefix)\n" + " --suffix str append the given string to the names of all extern,\n" + " common and global symbols (also --gprefix)\n" + " --lprefix str prepend the given string to local symbols\n" + " --lpostfix str append the given string to local symbols\n" + "\n" + " -w+x enable warning x (also -Wx)\n" + " -w-x disable warning x (also -Wno-x)\n" + " -w[+-]error promote all warnings to errors (also -Werror)\n" + " -w[+-]error=x promote warning x to errors (also -Werror=x)\n" + , out); + + fprintf(out, " %-20s %s\n", + warning_name[WARN_IDX_ALL], warning_help[WARN_IDX_ALL]); + + for (i = 1; i < WARN_IDX_ALL; i++) { + const char *me = warning_name[i]; + const char *prev = warning_name[i-1]; + const char *next = warning_name[i+1]; + + if (prev) { + int prev_len = strlen(prev); + const char *dash = me; + + while ((dash = strchr(dash+1, '-'))) { + int prefix_len = dash - me; /* Not including final dash */ + if (strncmp(next, me, prefix_len+1)) { + /* Only one or last option with this prefix */ + break; + } + if (prefix_len >= prev_len || + strncmp(prev, me, prefix_len) || + (prev[prefix_len] != '-' && prev[prefix_len] != '\0')) { + /* This prefix is different from the previous option */ + fprintf(out, " %-20.*s all warnings prefixed with \"%.*s\"\n", + prefix_len, me, prefix_len+1, me); + } + } } + + fprintf(out, " %-20s %s%s\n", + warning_name[i], warning_help[i], + (warning_default[i] & WARN_ST_ERROR) ? " [error]" : + (warning_default[i] & WARN_ST_ENABLED) ? " [on]" : " [off]"); } - printf("\nWarnings for the -W/-w options: (defaults in brackets)\n"); + fputs( + "\n" + " --limit-X val set execution limit X\n" + , out); - for (i = 1; i <= WARN_IDX_ALL; i++) { - printf(" %-23s %s%s\n", - warning_name[i], warning_help[i], - i == WARN_IDX_ALL ? "\n" : - (warning_default[i] & WARN_ST_ERROR) ? " [error]" : - (warning_default[i] & WARN_ST_ENABLED) ? " [on]" : " [off]"); - } - if (xopt == 'f') { - printf("valid output formats for -f are" - " (`*' denotes default):\n"); - ofmt_list(ofmt, stdout); - } else { - printf("For a list of valid output formats, use -hf.\n"); - printf("For a list of debug formats, use -f <format> -y.\n"); + for (i = 0; i <= LIMIT_MAX; i++) { + fprintf(out, " %-20s %s [", + limit_info[i].name, limit_info[i].help); + if (nasm_limit[i] < LIMIT_MAX_VAL) { + fprintf(out, "%"PRId64"]\n", nasm_limit[i]); + } else { + fputs("unlimited]\n", out); + } } } diff --git a/asm/preproc.c b/asm/preproc.c index 03176402..0b111e2f 100644 --- a/asm/preproc.c +++ b/asm/preproc.c @@ -342,9 +342,6 @@ enum { #define NO_DIRECTIVE_FOUND 0 #define DIRECTIVE_FOUND 1 -/* max reps */ -#define REP_LIMIT ((INT64_C(1) << 62)) - /* * Condition codes. Note that we use c_ prefix not C_ because C_ is * used in nasm.h for the "real" condition codes. At _this_ level, @@ -3000,8 +2997,7 @@ issue_error: return DIRECTIVE_FOUND; } defining = nasm_zalloc(sizeof(MMacro)); - defining->max_depth = ((i == PP_RMACRO) || (i == PP_IRMACRO)) - ? nasm_limit[LIMIT_MACROS] : 0; + defining->max_depth = nasm_limit[LIMIT_MACRO_LEVELS]; defining->casesense = casesense; if (!parse_mmacro_spec(tline, defining, dname)) { nasm_free(defining); @@ -4312,6 +4308,13 @@ static Token *expand_mmac_params(Token * tline) return thead; } +static Token *expand_smacro_noreset(Token * tline); +static struct { + int64_t tokens; + int64_t levels; + bool triggered; +} smacro_deadman; + /* * Expand *one* single-line macro instance. If the first token is not * a macro at all, it is simply copied to the output and the pointer @@ -4323,15 +4326,9 @@ static Token *expand_mmac_params(Token * tline) * If the expansion is empty, *tpp will be unchanged but **tpp will * be advanced past the macro call. * - * The return value equals **tpp. - * - * Return false if no expansion took place; true if it did. - * + * Return the macro expanded, or NULL if no expansion took place. */ -static Token *expand_smacro_noreset(Token * tline); -static int64_t smacro_deadman; - -static bool expand_one_smacro(Token ***tpp) +static SMacro *expand_one_smacro(Token ***tpp) { Token **params = NULL; const char *mname; @@ -4347,9 +4344,14 @@ static bool expand_one_smacro(Token ***tpp) mname = tline->text; - if (--smacro_deadman <= 0) { - if (smacro_deadman == 0) + smacro_deadman.tokens--; + smacro_deadman.levels--; + + if (unlikely(smacro_deadman.tokens < 0 || smacro_deadman.levels < 0)) { + if (unlikely(!smacro_deadman.triggered)) { nasm_nonfatal("interminable macro recursion"); + smacro_deadman.triggered = true; + } goto not_a_macro; } else if (tline->type == TOK_ID) { head = (SMacro *)hash_findix(&smacros, mname); @@ -4652,17 +4654,21 @@ static bool expand_one_smacro(Token ***tpp) /* Don't do this until after expansion or we will clobber mname */ free_tlist(mstart); - free_tlist_array(params, nparam); - return true; + goto done; /* * No macro expansion needed; roll back to mstart (if necessary) - * and then advance to the next input token. + * and then advance to the next input token. Note that this is + * by far the common case! */ not_a_macro: *tpp = &mstart->next; - free_tlist_array(params, nparam); - return false; + m = NULL; +done: + smacro_deadman.levels++; + if (unlikely(params)) + free_tlist_array(params, nparam); + return m; } /* @@ -4674,7 +4680,9 @@ not_a_macro: */ static Token *expand_smacro(Token *tline) { - smacro_deadman = nasm_limit[LIMIT_MACROS]; + smacro_deadman.tokens = nasm_limit[LIMIT_MACRO_TOKENS]; + smacro_deadman.levels = nasm_limit[LIMIT_MACRO_LEVELS]; + smacro_deadman.triggered = false; return expand_smacro_noreset(tline); } @@ -4718,10 +4726,8 @@ static Token *expand_smacro_noreset(Token * tline) }; tail = &thead; - while ((t = *tail)) { /* main token loop */ - - expanded |= expand_one_smacro(&tail); - } + while ((t = *tail)) /* main token loop */ + expanded |= !!expand_one_smacro(&tail); if (!expanded) { tline = thead; |