]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gas: introduce .errif and .warnif
authorJan Beulich <jbeulich@suse.com>
Fri, 4 Jul 2025 08:42:18 +0000 (10:42 +0200)
committerJan Beulich <jbeulich@suse.com>
Fri, 4 Jul 2025 08:42:18 +0000 (10:42 +0200)
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.

gas/NEWS
gas/doc/as.texi
gas/read.c
gas/read.h
gas/testsuite/gas/all/cond-diag.l [new file with mode: 0644]
gas/testsuite/gas/all/cond-diag.s [new file with mode: 0644]
gas/testsuite/gas/all/gas.exp
gas/write.c

index e48cee30bac449e745158b4fadb6019e7dd9f6be..4e93e3cf089a18995fdb9cbe6cb6a2a13aa8565c 100644 (file)
--- 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.
+
 * Predefined symbols "GAS(version)" and, on non-release builds, "GAS(date)" are
   now being made available.
 
index e17c2df40db6c4fb1ecb3a1190b8fa1b60b92a92..70c25405f874766b932b2c142492bc981fa0ab8b 100644 (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
index db9c76270ee734a95f6c7b25bfcfeeec5b875368..10425a7279035e3663f28163e27cae9d6b2446a7 100644 (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
index b723f4a7c5fb5425c886494ffcc51e7e6bb34a3f..72b66cdd95214609c5936856555c51fd2b78c793 100644 (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;
diff --git a/gas/testsuite/gas/all/cond-diag.l b/gas/testsuite/gas/all/cond-diag.l
new file mode 100644 (file)
index 0000000..6b802c6
--- /dev/null
@@ -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 .*
diff --git a/gas/testsuite/gas/all/cond-diag.s b/gas/testsuite/gas/all/cond-diag.s
new file mode 100644 (file)
index 0000000..0c02f15
--- /dev/null
@@ -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:
index 41d35285c7beb7765502d05ddf78d108a94204db..81f4db24fadbb2f120457562053ebca99d5c2c67 100644 (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"
index 8ccd996089cadf8fec7f8cb71ea85c68655e3acc..4d95d769d452fef856f93dad657d55aa55d8d559 100644 (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 ();