[PATCH RFC 3/3] gas: introduce .errif and .warnif
Fangrui Song
i@maskray.me
Wed Dec 11 07:13:32 GMT 2024
On Mon, Dec 9, 2024 at 1:27 AM Jan Beulich <jbeulich@suse.com> wrote:
>
> 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
> has their final values established, provide directives to directly
> achieve this.
Perhaps allow an optional message argument similar to static_assert?
https://en.cppreference.com/w/c/language/_Static_assert
https://en.cppreference.com/w/cpp/language/static_assert
> ---
> Should we save the (textual) expression, to quote it when emitting the
> diagnostic?
>
> NOTE: Depends on "aarch64: re-work PR gas/27217 fix again"
> (latched_dot_expression()), unless that would be re-worked such
> that deferred_expression() would need using here.
>
> --- a/gas/NEWS
> +++ b/gas/NEWS
> @@ -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.
> +
> * Add support for the x86 Intel MSR_IMM instructions.
>
> * Add support for the x86 Zhaoxin GMI instructions.
> --- a/gas/doc/as.texi
> +++ b/gas/doc/as.texi
> @@ -4577,6 +4577,7 @@ Some machine configurations provide addi
> * 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}
> @@ -4709,6 +4710,7 @@ Some machine configurations provide addi
> * 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}}
> @@ -5529,6 +5531,13 @@ If @command{@value{AS}} assembles a @cod
> 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
> @@ -7714,6 +7723,13 @@ parent whose addend is the value of 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
> --- a/gas/read.c
> +++ b/gas/read.c
> @@ -257,6 +257,7 @@ static void do_s_func (int end_p, const
> static void s_align (int, int);
> 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);
> @@ -415,6 +416,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 */
> @@ -529,6 +531,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},
> @@ -2236,6 +2239,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 ();
> +
> + latched_dot_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
> --- a/gas/read.h
> +++ b/gas/read.h
> @@ -162,6 +162,7 @@ extern symbolS *s_comm_internal (int, sy
> 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;
> --- a/gas/write.c
> +++ b/gas/write.c
> @@ -2329,6 +2329,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 ();
> --- /dev/null
> +++ b/gas/testsuite/gas/all/cond-diag.l
> @@ -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 .*
> --- /dev/null
> +++ b/gas/testsuite/gas/all/cond-diag.s
> @@ -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:
> --- a/gas/testsuite/gas/all/gas.exp
> +++ b/gas/testsuite/gas/all/gas.exp
> @@ -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"
>
More information about the Binutils
mailing list