[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