From fe5235ffc9bcb649fc6f17df852c77845bb5bbf8 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin (Intel)" Date: Thu, 9 Apr 2026 16:56:01 -0700 Subject: [PATCH] limits: allow querying limits, and reset limits for each pass Allow limits to be queried via a %limit() preprocessor function and a __?NASM_LIMITS?__ macro. Reset limits at the top of each pass. Note that the pass number limits are checked at the *end* of the pass, so a %pragma limit will be properly honored anyway. Signed-off-by: H. Peter Anvin (Intel) --- asm/nasm.c | 58 ++++++++++++++++++++++++++++++++++++++----- asm/preproc.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/changes.src | 3 ++- doc/preproc.src | 39 +++++++++++++++++++++++++++++ doc/running.src | 8 ++++++ doc/stdmac.src | 14 +++++++++++ include/nasm.h | 11 +++++++++ test/limits.asm | 32 ++++++++++++++++++++++++ 8 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 test/limits.asm diff --git a/asm/nasm.c b/asm/nasm.c index 6db219455..5da8a8206 100644 --- a/asm/nasm.c +++ b/asm/nasm.c @@ -129,9 +129,8 @@ static char *(*quote_for_make)(const char *) = quote_for_pmake; * 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) /* Absolute maximum for any limit */ - int64_t nasm_limit[LIMIT_MAX]; +static int64_t nasm_limit_from_cmdline[LIMIT_MAX]; struct limit_info { const char *name; @@ -222,7 +221,7 @@ enum directive_result nasm_set_limit(const char *limit, const char *valstr) { int i; - int64_t val, default_val, max_val; + int64_t val, init_val, default_val, max_val; bool rn_error; int errlevel; @@ -246,8 +245,15 @@ nasm_set_limit(const char *limit, const char *valstr) max_val = limit_max(i); default_val = limit_default(i); + init_val = nasm_limit_from_cmdline[i]; + if (!init_val) + init_val = default_val; - if (!*valstr || !nasm_stricmp(valstr, "default")) { + if (!strcmp(valstr, "*") || + !nasm_stricmp(valstr, "reset") || + !nasm_stricmp(valstr, "init")) { + val = init_val; + } else if (!nasm_stricmp(valstr, "default")) { val = default_val; } else if (!nasm_stricmp(valstr, "unlimited") || !nasm_stricmp(valstr, "maximum") || @@ -267,7 +273,7 @@ nasm_set_limit(const char *limit, const char *valstr) } if (!val) - val = default_val; + val = init_val; if (val > max_val) { val = max_val; @@ -284,6 +290,43 @@ nasm_set_limit(const char *limit, const char *valstr) return DIRR_OK; } +int64_t nasm_get_limit(const char *limit, enum get_limit_which which) +{ + int i; + + for (i = 0; i < LIMIT_MAX; i++) { + if (!nasm_stricmp(limit, limit_info[i].name)) { + switch (which) { + case GET_LIMIT_CURRENT: + return nasm_limit[i]; + case GET_LIMIT_INIT: + return nasm_limit_from_cmdline[i]; + case GET_LIMIT_DEFAULT: + return limit_default(i); + case GET_LIMIT_MAX: + return limit_max(i); + default: + return 0; + } + } + } + + return 0; +} + +const char *nasm_limit_name(enum nasm_limit i) +{ + if (i >= LIMIT_MAX) + return NULL; + + return limit_info[i].name; +} + +static void reset_limits_before_pass(void) +{ + memcpy(nasm_limit, nasm_limit_from_cmdline, sizeof nasm_limit); +} + int64_t switch_segment(int32_t segment) { location.segment = segment; @@ -601,8 +644,9 @@ int main(int argc, char **argv) if (terminate_after_phase()) return 1; - /* Save away the default state of warnings */ + /* Save away the default states of warnings and limits */ error_init(); + memcpy(nasm_limit_from_cmdline, nasm_limit, sizeof nasm_limit); /* Dependency filename if we are also doing other things */ if (!depend_file && (operating_mode & ~OP_DEPEND)) { @@ -1638,6 +1682,8 @@ static void assemble_file(const char *fname, struct strlist *depend_list) insn output_ins; uint64_t prev_offset_changed; + reset_limits_before_pass(); + switch (cmd_sb) { case 16: break; diff --git a/asm/preproc.c b/asm/preproc.c index 5c9bdd0b8..94aef7c20 100644 --- a/asm/preproc.c +++ b/asm/preproc.c @@ -8198,6 +8198,46 @@ stdmac_env(const SMacro *s, Token **params, int nparam) return make_tok_qstr(NULL, env); } +static Token * +stdmac_limit(const SMacro *s, Token **params, int nparam) +{ + const char *which_str, *limit; + enum get_limit_which which; + int64_t val = 0; + + (void)s; + (void)nparam; + + which_str = unquote_token(params[1]); + + if (!*which_str || !nasm_stricmp(which_str, "current")) { + which = GET_LIMIT_CURRENT; + } else if (!strcmp(which_str, "*") || + !nasm_stricmp(which_str, "reset") || + !nasm_stricmp(which_str, "init")) { + which = GET_LIMIT_INIT; + } else if (!nasm_stricmp(which_str, "default")) { + which = GET_LIMIT_DEFAULT; + } else if (!nasm_stricmp(which_str, "unlimited") || + !nasm_stricmp(which_str, "maximum") || + !nasm_stricmp(which_str, "max")) { + which = GET_LIMIT_MAX; + } else { + nasm_nonfatal("invalid second argument `%s' to %s()", + which_str, s->name); + goto err; + } + + limit = unquote_token(params[0]); + if (!*limit || !nasm_stricmp(limit, "unlimited")) + val = LIMIT_MAX_VAL; + else + val = nasm_get_limit(limit, which); + +err: + return make_tok_num(NULL, val); +} + /* * Wrapper around define_smacro() which also checks to see if it is * a preprocessor directive, so that pp_op_may_be_function[] needs to @@ -8407,6 +8447,16 @@ static void pp_add_magic_miscfunc(void) tmpl.params[1].def = make_tok_qstr_len(NULL, "", 0); define_magic("%b2hs", false, &tmpl); + /* %limit() function */ + nasm_zero(tmpl); + tmpl.nparam = 2; + tmpl.expand = stdmac_limit; + tmpl.recursive = true; + nasm_newn(tmpl.params, tmpl.nparam); + tmpl.params[0].flags = SPARM_STR|SPARM_CONDQUOTE; + tmpl.params[1].flags = SPARM_STR|SPARM_CONDQUOTE|SPARM_OPTIONAL; + define_magic("%limit", false, &tmpl); + /* %env() function */ for (i = 1; i <= 2; i++) { nasm_zero(tmpl); @@ -8469,6 +8519,20 @@ static void pp_start_stdmac(void) } } +static void pp_add_limits_stdmac(void) +{ + int i; + Token *t = NULL; + + for (i = LIMIT_MAX-1; i > 0; i--) { + t = make_tok_qstr(t, nasm_limit_name(i)); + t = make_tok_char(t, ','); + } + t = make_tok_qstr(t, nasm_limit_name(0)); + + define_smacro("__?NASM_LIMITS?__", true, t, NULL); +} + static void pp_reset_stdmac(enum preproc_mode mode) { int apass; @@ -8484,6 +8548,8 @@ static void pp_reset_stdmac(enum preproc_mode mode) pp_add_stdmac(&nasm_stdmac_version); pp_add_stdmac(ofmt->stdmac); + pp_add_limits_stdmac(); + do_predef = true; /* diff --git a/doc/changes.src b/doc/changes.src index c265fc938..4eda80573 100644 --- a/doc/changes.src +++ b/doc/changes.src @@ -110,7 +110,8 @@ It is the production version of NASM since 2025. configurable limit. See \k{opt-limit}. \b The \c{--limit-} options and \c{%pragma limit} now accept the - keywords \c{default} and \c{maximum}. See \k{opt-limit}. + keywords \c{default}, \c{maximum}, and \c{reset}. See + \k{opt-limit}. \S{cl-3.01} Version 3.01 diff --git a/doc/preproc.src b/doc/preproc.src index 522330f2e..149d612da 100644 --- a/doc/preproc.src +++ b/doc/preproc.src @@ -888,6 +888,45 @@ Unlike the C \c{defined()} preprocessor construct, these functions are valid anywhere in the source code, not just in \c{%if} expressions. +\S{f_limit} \i\c{%limit()} Function + +The \c{%limit()} function takes as its first argument an optionally +quoted string which is matched against a resource limit as defined by +the \c{--limit-} command line option or the \c{%pragma limit} +directive (see \k{opt-limit}). The macro then expands to the value of +that limit, or \c{0} if no limit with that name is known in the +current version of NASM. + +An optional second argument can be set to one of the following +optionally quoted strings: + +\b \c{current}: the current value for the limit. This is also the result + if no second argument is specified. + +\b \e{reset}: the initial value for the limit, set on the command line + or the default value if no such value is set; this is the value + that \c{%pragma limit} \e{limit-name} \c{reset} would set the limit + to. + +\b \e{default}: the default value for the limit. This is the + value that \c{%pragma limit} \e{limit-name} \c{default} would set + the limit to. + +\b \e{maximum}: the maximum permitted value for the limit. This is the + value that \c{%pragma limit} \e{limit-name} \c{maximum} would set + the limit to. + +The value \c{unlimited} is represented by a very large positive +number. If the limit name is the empty string or \c{unlimited}, +\c{%limit()} returns this value for comparison purposes. + +The standard macro \c{__?NASM_LIMITS?__} expands to a comma-separated +list of quoted strings representing all limits defined in the current +version of NASM, see \k{nasm_limits}. + +The \c{%limit()} function was introduced in NASM 3.02. + + \S{f_map} \i\c{%map()} Function The \c{%map()} function takes as its first parameter the name of a diff --git a/doc/running.src b/doc/running.src index 7fd8e31b5..d184f5429 100644 --- a/doc/running.src +++ b/doc/running.src @@ -649,6 +649,14 @@ example: \c %pragma limit lines 1000 +Specifying the limit value as \c{*} or \c{reset} resets the limit to +the value specified on the command line or the default value, undoing +any previous \c{%pragma limit}. + +See also the \c{%limit()} preprocessor function (\k{f_limit}) and the +\c{__?NASM_LIMITS?__} standard macro (\k{nasm_limits}). + + \IR{--keep-all} \c{--keep-all} option \S{opt-keep-all} The \i\c{--keep-all} Option diff --git a/doc/stdmac.src b/doc/stdmac.src index 21fcfd133..eca17cbb2 100644 --- a/doc/stdmac.src +++ b/doc/stdmac.src @@ -184,6 +184,20 @@ clock: \c __?UTC_TIME_NUM?__ 210042 \c __?POSIX_TIME?__ 1262293242 + +\H{nasm_limits} \i\c{__?NASM_LIMITS?__}: List of Resource Limits + +The standard macro \c{__?NASM_LIMITS?__} is defined as a +comma-separated list of quoted strings corresponding to all resource +limits defined in the current version of NASM. It also indicates the +availability of the \c{%limit()} function and the keywords +\c{default}, \c{maximum}, and \c{reset} for \c{%pragma limit}. + +See \k{opt-limit} and \c{f_limit}. + +\c{__?NASM_LIMITS?__} is defined since NASM 3.02. + + \H{has_ifdirective} \i\c{__?NASM_HAS_IFDIRECTIVE?__}: Directive Probing Support diff --git a/include/nasm.h b/include/nasm.h index af2893fba..e81c3ec82 100644 --- a/include/nasm.h +++ b/include/nasm.h @@ -964,9 +964,20 @@ enum nasm_limit { LIMIT_MAX /* Must be at end */ }; +#define LIMIT_MAX_VAL (INT64_MAX >> 1) /* Absolute maximum for any limit */ + extern int64_t nasm_limit[LIMIT_MAX]; extern enum directive_result nasm_set_limit(const char *, const char *); +enum get_limit_which { + GET_LIMIT_CURRENT, + GET_LIMIT_INIT, + GET_LIMIT_DEFAULT, + GET_LIMIT_MAX +}; +extern int64_t nasm_get_limit(const char *, enum get_limit_which); +extern const char *nasm_limit_name(enum nasm_limit); + /* * The data structure defining an output format driver, and the * interfaces to the functions therein. diff --git a/test/limits.asm b/test/limits.asm new file mode 100644 index 000000000..f136732cc --- /dev/null +++ b/test/limits.asm @@ -0,0 +1,32 @@ +limits: + dq %limit() ; unlimited + dq %limit(unlimited) + dq %limit("unlimited") + + dq %limit("passes") + dq %limit("passes","current") + dq %limit("passes","reset") + dq %limit("passes","default") + dq %limit("passes","maximum") + + dq %limit("params") + dq %limit("params","current") + dq %limit("params","reset") + dq %limit("params","default") + dq %limit("params","maximum") + +%pragma limit params 9999 + + dq %limit("params") + dq %limit("params","current") + dq %limit("params","reset") + dq %limit("params","default") + dq %limit("params","maximum") + +%pragma limit params reset + + dq %limit("params") + dq %limit("params","current") + dq %limit("params","reset") + dq %limit("params","default") + dq %limit("params","maximum")