gas: introduce .errif and .warnif

Rather than having people resort to indirect means to issue a certain
kind of diagnostic conditionally upon an expression which can (or
should) only be evaluated when all sections were sized and all symbols
had their final values established, provide directives to directly
achieve this.
This commit is contained in:
Jan Beulich
2025-07-04 10:42:18 +02:00
parent 7b40f4c658
commit 42c97689a5
8 changed files with 112 additions and 0 deletions

View File

@@ -1,5 +1,8 @@
-*- text -*-
* Add .errif and .warnif directives, permitting user-controlled diagnostics
with conditionals that are evaluated only at the end of assembly.
* Predefined symbols "GAS(version)" and, on non-release builds, "GAS(date)" are
now being made available.

View File

@@ -4582,6 +4582,7 @@ Some machine configurations provide additional directives.
* Equiv:: @code{.equiv @var{symbol}, @var{expression}}
* Eqv:: @code{.eqv @var{symbol}, @var{expression}}
* Err:: @code{.err}
* Errif:: @code{.errif @var{expression}}
* Error:: @code{.error @var{string}}
* Exitm:: @code{.exitm}
* Extern:: @code{.extern}
@@ -4714,6 +4715,7 @@ Some machine configurations provide additional directives.
* VTableInherit:: @code{.vtable_inherit @var{child}, @var{parent}}
@end ifset
* Warnif:: @code{.warnif @var{expression}}
* Warning:: @code{.warning @var{string}}
* Weak:: @code{.weak @var{names}}
* Weakref:: @code{.weakref @var{alias}, @var{symbol}}
@@ -5557,6 +5559,13 @@ If @command{@value{AS}} assembles a @code{.err} directive, it will print an erro
message and, unless the @option{-Z} option was used, it will not generate an
object file. This can be used to signal an error in conditionally compiled code.
@node Errif
@section @code{.errif "@var{expression}"}
@cindex errif directive
Record @var{expression} for evaluation at the end of assembly. Raise an error
if the expression evaluates to non-zero.
@node Error
@section @code{.error "@var{string}"}
@cindex error directive
@@ -7750,6 +7759,13 @@ parent whose addend is the value of the child symbol. As a special case the
parent name of @code{0} is treated as referring to the @code{*ABS*} section.
@end ifset
@node Warnif
@section @code{.warnif "@var{expression}"}
@cindex errif directive
Record @var{expression} for evaluation at the end of assembly. Raise a
warning if the expression evaluates to non-zero.
@node Warning
@section @code{.warning "@var{string}"}
@cindex warning directive

View File

@@ -236,6 +236,7 @@ static unsigned int bundle_lock_depth;
static void do_s_func (int end_p, const char *default_prefix);
static void s_altmacro (int);
static void s_bad_end (int);
static void s_errwarn_if (int);
static void s_reloc (int);
static int hex_float (int, char *);
static segT get_known_segmented_expression (expressionS * expP);
@@ -401,6 +402,7 @@ static const pseudo_typeS potable[] = {
{"equiv", s_set, 1},
{"eqv", s_set, -1},
{"err", s_err, 0},
{"errif", s_errwarn_if, 1},
{"error", s_errwarn, 1},
{"exitm", s_mexit, 0},
/* extend */
@@ -515,6 +517,7 @@ static const pseudo_typeS potable[] = {
{"xdef", s_globl, 0},
{"xref", s_ignore, 0},
{"xstabs", s_xstab, 's'},
{"warnif", s_errwarn_if, 0},
{"warning", s_errwarn, 0},
{"weakref", s_weakref, 0},
{"word", cons, 2},
@@ -2237,6 +2240,62 @@ s_errwarn (int err)
demand_empty_rest_of_line ();
}
/* Handle the .errif and .warnif pseudo-ops. */
static struct deferred_diag {
struct deferred_diag *next;
const char *file;
unsigned int lineno;
bool err;
expressionS exp;
} *deferred_diags, *last_deferred_diag;
static void
s_errwarn_if (int err)
{
struct deferred_diag *diag = XNEW (struct deferred_diag);
int errcnt = had_errors ();
deferred_expression (&diag->exp);
if (errcnt != had_errors ())
{
ignore_rest_of_line ();
return;
}
diag->err = err;
diag->file = as_where (&diag->lineno);
diag->next = NULL;
if ( deferred_diags == NULL )
deferred_diags = diag;
else
last_deferred_diag->next = diag;
last_deferred_diag = diag;
demand_empty_rest_of_line ();
}
void
evaluate_deferred_diags (void)
{
struct deferred_diag *diag;
for (diag = deferred_diags; diag != NULL; diag = diag->next)
{
if (!resolve_expression (&diag->exp) || diag->exp.X_op != O_constant)
as_warn_where (diag->file, diag->lineno,
_("expression does not evaluate to a constant"));
else if (diag->exp.X_add_number == 0)
continue;
else if (diag->err)
as_bad_where (diag->file, diag->lineno,
_(".errif expression evaluates to true"));
else
as_warn_where (diag->file, diag->lineno,
_(".warnif expression evaluates to true"));
}
}
/* Handle the MRI fail pseudo-op. */
void

View File

@@ -169,6 +169,7 @@ extern symbolS *s_comm_internal (int, symbolS *(*) (int, symbolS *, addressT));
extern symbolS *s_lcomm_internal (int, symbolS *, addressT);
extern void temp_ilp (char *);
extern void restore_ilp (void);
extern void evaluate_deferred_diags (void);
extern void s_file_string (char *);
extern void s_abort (int) ATTRIBUTE_NORETURN;

View File

@@ -0,0 +1,6 @@
# This should match the output of gas cond-diag.s.
.*: Assembler messages:
.*:1: Error: non-constant .*
.*:6: Error: backward ref .*
.*:7: Warning: \.warning .*
.*:4: Warning: \.warnif .*

View File

@@ -0,0 +1,12 @@
.if end - start > 16
.warning
.endif
.warnif end - start < 16
.errif end - start >= 16
.warnif 1b
.warning
.data
start:
.uleb128 end - start
end:

View File

@@ -488,6 +488,19 @@ switch -glob $target_triplet {
}
}
# This test uses a local label, which some targets don't support.
# MeP can't deal with forward ref labels in .uleb128.
switch -glob $target_triplet {
*c54x*-*-* { }
hppa*-*-* { }
ia64-*-*vms* { }
mep-*-* { }
sh-*-pe* { }
default {
run_list_test "cond-diag"
}
}
gas_test_error "weakref2.s" "" "e: would close weakref loop: e => a => b => c => d => e"
gas_test_error "weakref3.s" "" "a: would close weakref loop: a => b => c => d => e => a"
gas_test_error "weakref4.s" "" "is already defined"

View File

@@ -2321,6 +2321,8 @@ write_object_file (void)
resolve_local_symbol_values ();
resolve_reloc_expr_symbols ();
evaluate_deferred_diags ();
#ifdef OBJ_ELF
if (IS_ELF)
maybe_generate_build_notes ();