This adjusts equate handling by
- allowing true forward references (which will always assume the referenced
  symbols have at the point of use) through the new .eqv pseudo-op and the
  new == operator
- disallowing changing .equiv-generated equates (so that the protection this
  provides is both forward and backward)
- snapshotting equates when their value gets changed so that previous uses
  don't get affected by the new value.
- allowing expressions in places where absolute expressions (or register
  names) are needed which were not completely resolvable at the point of
  their definition but which are fully resolvable at the point of use

In addition it fixes PR/288.

Built and tested on i686-pc-linux-gnu, x86_64-unknown-linux-gnu,
ia64-unknown-linux-gnu, and for a large number of cross targets.

Jan

gas/
2005-09-29  Jan Beulich  <jbeulich@novell.com>

	* expr.h (enum expr_mode): New.
	(expression): Pass thrid argument to expr.
	(expression_and_evaluate): New.
	(deferred_expression): New.
	(expr): Add third parameter.
	(resolve_expression): New.
	* struc-symbol.h (struct symbol): New members sy_volatile and
	sy_forward_ref.
	* symbols.c, symbols.h (symbol_clone): New.
	(symbol_clone_if_forward_ref): New.
	(snapshot_symbol): New.
	(S_IS_VOLATILE): New.
	(S_IS_FORWARD_REF): New.
	(S_SET_VOLATILE): New.
	(S_SET_FORWARD_REF): New.
	* as.c (macro_expr): Use expression_and_evaluate.
	* cond.c (s_if): Likewise.
	(s_elseif): Likewise.
	* dw2gencfi.c (cfi_parse_reg): Likewise.
	* expr.c (operand): Add second parameter. Optionally call
	deferred_expression. Pass mode argument to itself and md_parse_name.
	Check mode before trying to evaluate symbol. Call
	symbol_clone_if_forward_ref for both operands.
	(expr): Add thrid parameter. Pass mode to operand and itself.
	Optionally call resolve_expression.
	(resolve_expression): New.
	(get_single_number): Pass second argument to operand.
	* read.c (potable): New entry for .eqv.
	(read_a_source_file): Handle new == operator.
	(get_absolute_expr): Use expression_and_evaluate.
	(s_lsym): Likewise.
	(assign_symbol): Rename second parameter. Call symbol_clone on
	legal and illegal redefinition. Call S_SET_VOLATILE and
	S_SET_FORWARD_REF depending on mode.
	(s_set): Update description.
	(s_space): Call resolve_expression.
	(pseudo_set): Optionally call deferred_expression. Check
	S_IS_FORWARD_REF before trying to simplify/resolve an expression.
	(equals): Handle ==.
	* config/tc-ia64.h (md_parse_name): Add mode parameter.
	* config/tc-m32r.h (md_parse_name): Likewise.
	(m32r_parse_name): Likewise.
	* config/tc-mmix.h (md_parse_name): Likewise.
	* config/tc-mn10300.h (md_parse_name): Likewise.
	(mn10300_parse_name): Likewise.
	* config/tc-ppc.h (md_parse_name): Likewise.
	* config/tc-sh.h (md_parse_name): Likewise.
	(sh_parse_name): Likewise.
	* config/tc-sh64.h (md_parse_name): Likewise.
	(sh64_consume_datalabel): Likewise.
	* config/tc-tic54x.h (md_parse_name): Likewise.
	* config/tc-m32r.c (m32r_parse_name): Add mode parameter. Check it
	before trying to evaluate symbol.
	* config/tc-mn10300.c (mn10300_parse_name): Likewise.
	* config/tc-sh.c (sh_parse_name): Likewise.
	* config/tc-sh64.c (sh64_consume_datalabel): Add mode parameter. Pass
	second argument to operandf. Pass mode parameter to sh_parse_name.
	* doc/as.texinfo: Document .eqv and the == assignment operator.

gas/testsuite/
2005-09-29  Jan Beulich  <jbeulich@novell.com>

	* gas/all/cond.s: Add test for resolution of fully resolvable
	forward references in .if/.endif.
	* gas/all/cond.d: Adjust.
	* gas/all/assign-bad.s: New.
	* gas/all/assign-ok.s: New.
	* gas/all/equ-bad.s: New.
	* gas/all/equ-ok.s: New.
	* gas/all/equiv1.s: New.
	* gas/all/equiv2.s: New.
	* gas/all/eqv-bad.s: New.
	* gas/all/eqv-ok.s: New.
	* gas/all/forward.[sd]: New.
	* gas/all/redef.[sd]: New.
	* gas/all/gas.exp: Run new tests, but xfail equiv1 (PR/1387).

--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/as.c	2005-08-18 08:51:25.000000000 +0200
+++ 2005-09-27/gas/as.c	2005-09-27 09:40:34.000000000 +0200
@@ -940,7 +940,7 @@ macro_expr (const char *emsg, int idx, s
 
   hold = input_line_pointer;
   input_line_pointer = in->ptr + idx;
-  expression (&ex);
+  expression_and_evaluate (&ex);
   idx = input_line_pointer - in->ptr;
   input_line_pointer = hold;
 
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/cond.c	2005-05-06 08:34:42.000000000 +0200
+++ 2005-09-27/gas/cond.c	2005-09-26 11:09:57.000000000 +0200
@@ -144,7 +144,7 @@ s_if (int arg)
     }
   else
     {
-      expression (&operand);
+      expression_and_evaluate (&operand);
       if (operand.X_op != O_constant)
 	as_bad (_("non-constant expression in \".if\" statement"));
     }
@@ -340,7 +340,7 @@ s_elseif (int arg)
       /* Leading whitespace is part of operand.  */
       SKIP_WHITESPACE ();
 
-      expression (&operand);
+      expression_and_evaluate (&operand);
       if (operand.X_op != O_constant)
 	as_bad (_("non-constant expression in \".elseif\" statement"));
 
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-ia64.h	2005-07-05 13:53:32.000000000 +0200
+++ 2005-09-27/gas/config/tc-ia64.h	2005-09-28 14:50:43.000000000 +0200
@@ -129,7 +129,7 @@ extern void ia64_convert_frag (fragS *);
 #define tc_frob_symbol(s,p)		p |= ia64_frob_symbol (s)
 #endif /* TE_HPUX */
 #define md_flush_pending_output()	ia64_flush_pending_output ()
-#define md_parse_name(s,e,c)		ia64_parse_name (s, e, c)
+#define md_parse_name(s,e,m,c)		ia64_parse_name (s, e, c)
 #define tc_canonicalize_symbol_name(s)	ia64_canonicalize_symbol_name (s)
 #define tc_canonicalize_section_name(s)	ia64_canonicalize_symbol_name (s)
 #define md_optimize_expr(l,o,r)		ia64_optimize_expr (l, o, r)
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-m32r.c	2005-08-18 08:51:28.000000000 +0200
+++ 2005-09-27/gas/config/tc-m32r.c	2005-09-28 14:40:37.000000000 +0200
@@ -2370,7 +2370,10 @@ m32r_end_of_match (char *cont, char *wha
 }
 
 int
-m32r_parse_name (char const *name, expressionS *exprP, char *nextcharP)
+m32r_parse_name (char const *name,
+		 expressionS *exprP,
+		 enum expr_mode mode,
+		 char *nextcharP)
 {
   char *next = input_line_pointer;
   char *next_end;
@@ -2391,13 +2394,13 @@ m32r_parse_name (char const *name, expre
       /* If we have an absolute symbol or a
 	 reg, then we know its value now.  */
       segment = S_GET_SEGMENT (exprP->X_add_symbol);
-      if (segment == absolute_section)
+      if (mode != expr_defer && segment == absolute_section)
 	{
 	  exprP->X_op = O_constant;
 	  exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
 	  exprP->X_add_symbol = NULL;
 	}
-      else if (segment == reg_section)
+      else if (mode != expr_defer && segment == reg_section)
 	{
 	  exprP->X_op = O_register;
 	  exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-m32r.h	2005-08-18 08:51:28.000000000 +0200
+++ 2005-09-27/gas/config/tc-m32r.h	2005-09-28 14:48:35.000000000 +0200
@@ -121,9 +121,9 @@ extern void m32r_flush_pending_output (v
 #define elf_tc_final_processing 	m32r_elf_final_processing
 extern void m32r_elf_final_processing (void);
 
-#define md_parse_name(name, exprP, nextcharP) \
-  m32r_parse_name ((name), (exprP), (nextcharP))
-extern int m32r_parse_name (char const *, expressionS *, char *);
+#define md_parse_name(name, exprP, mode, nextcharP) \
+  m32r_parse_name ((name), (exprP), (mode), (nextcharP))
+extern int m32r_parse_name (char const *, expressionS *, enum expr_mode, char *);
 
 /* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
    symbols.  The relocation type is stored in X_md.  */
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-mmix.h	2005-05-06 08:24:28.000000000 +0200
+++ 2005-09-27/gas/config/tc-mmix.h	2005-09-28 14:52:43.000000000 +0200
@@ -70,7 +70,7 @@ extern char *mmix_current_prefix;
    The [DVWIOUZX]_Handler symbols are provided when-used.  */
 
 extern int mmix_gnu_syntax;
-#define md_parse_name(name, exp, cpos)				\
+#define md_parse_name(name, exp, mode, cpos)			\
  (! mmix_gnu_syntax						\
   && (name[0] == '@'						\
       ? (! is_part_of_name (name[1])				\
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-mn10300.c	2005-08-18 08:51:29.000000000 +0200
+++ 2005-09-27/gas/config/tc-mn10300.c	2005-09-28 14:54:14.000000000 +0200
@@ -2743,9 +2743,10 @@ mn10300_end_of_match (cont, what)
 }  
 
 int
-mn10300_parse_name (name, exprP, nextcharP)
+mn10300_parse_name (name, exprP, mode, nextcharP)
      char const *name;
      expressionS *exprP;
+     enum expr_mode mode;
      char *nextcharP;
 {
   char *next = input_line_pointer;
@@ -2765,13 +2766,13 @@ mn10300_parse_name (name, exprP, nextcha
       /* If we have an absolute symbol or a reg,
 	 then we know its value now.  */
       segment = S_GET_SEGMENT (exprP->X_add_symbol);
-      if (segment == absolute_section)
+      if (mode != expr_defer && segment == absolute_section)
 	{
 	  exprP->X_op = O_constant;
 	  exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
 	  exprP->X_add_symbol = NULL;
 	}
-      else if (segment == reg_section)
+      else if (mode != expr_defer && segment == reg_section)
 	{
 	  exprP->X_op = O_register;
 	  exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-mn10300.h	2005-08-18 08:51:29.000000000 +0200
+++ 2005-09-27/gas/config/tc-mn10300.h	2005-09-28 14:53:36.000000000 +0200
@@ -36,9 +36,10 @@
 	   && S_IS_DEFINED ((FIX)->fx_addsy)			\
 	   && ! S_IS_COMMON ((FIX)->fx_addsy))))
 
-#define md_parse_name(name, exprP, nextcharP) \
-    mn10300_parse_name ((name), (exprP), (nextcharP))
-int mn10300_parse_name PARAMS ((char const *, expressionS *, char *));
+#define md_parse_name(name, exprP, mode, nextcharP) \
+    mn10300_parse_name ((name), (exprP), (mode), (nextcharP))
+int mn10300_parse_name PARAMS ((char const *, expressionS *,
+				enum expr_mode, char *));
 
 #define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP) \
      mn10300_cons_fix_new ((FRAG), (OFF), (LEN), (EXP))
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-ppc.h	2005-08-18 08:51:30.000000000 +0200
+++ 2005-09-27/gas/config/tc-ppc.h	2005-09-28 14:43:18.000000000 +0200
@@ -245,7 +245,7 @@ extern int ppc_force_relocation PARAMS (
 #define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC)
 extern long md_pcrel_from_section PARAMS ((struct fix *, segT));
 
-#define md_parse_name(name, exp, c) ppc_parse_name (name, exp)
+#define md_parse_name(name, exp, mode, c) ppc_parse_name (name, exp)
 extern int ppc_parse_name PARAMS ((const char *, struct expressionS *));
 
 #define md_operand(x)
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-sh.c	2005-09-26 08:41:25.000000000 +0200
+++ 2005-09-27/gas/config/tc-sh.c	2005-09-28 14:45:04.000000000 +0200
@@ -4246,7 +4246,10 @@ sh_end_of_match (char *cont, char *what)
 }
 
 int
-sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
+sh_parse_name (char const *name,
+	       expressionS *exprP,
+	       enum expr_mode mode,
+	       char *nextcharP)
 {
   char *next = input_line_pointer;
   char *next_end;
@@ -4265,13 +4268,13 @@ sh_parse_name (char const *name, express
       /* If we have an absolute symbol or a reg, then we know its
 	     value now.  */
       segment = S_GET_SEGMENT (exprP->X_add_symbol);
-      if (segment == absolute_section)
+      if (mode != expr_defer && segment == absolute_section)
 	{
 	  exprP->X_op = O_constant;
 	  exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
 	  exprP->X_add_symbol = NULL;
 	}
-      else if (segment == reg_section)
+      else if (mode != expr_defer && segment == reg_section)
 	{
 	  exprP->X_op = O_register;
 	  exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-sh.h	2005-09-26 08:41:25.000000000 +0200
+++ 2005-09-27/gas/config/tc-sh.h	2005-09-28 14:47:22.000000000 +0200
@@ -206,9 +206,10 @@ extern bfd_boolean sh_fix_adjustable (st
   ((FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL		\
    || (sh_relax && SWITCH_TABLE (FIX)))
 
-#define md_parse_name(name, exprP, nextcharP) \
-  sh_parse_name ((name), (exprP), (nextcharP))
-int sh_parse_name (char const *name, expressionS *exprP, char *nextchar);
+#define md_parse_name(name, exprP, mode, nextcharP) \
+  sh_parse_name ((name), (exprP), (mode), (nextcharP))
+int sh_parse_name (char const *name, expressionS *exprP,
+		   enum expr_mode mode, char *nextchar);
 
 #define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP) \
   sh_cons_fix_new ((FRAG), (OFF), (LEN), (EXP))
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-sh64.c	2005-08-22 14:01:31.000000000 +0200
+++ 2005-09-27/gas/config/tc-sh64.c	2005-09-28 14:48:01.000000000 +0200
@@ -3244,8 +3244,9 @@ sh64_frob_label (symbolS *symp)
    symbol hook.  */
 
 int
-sh64_consume_datalabel (const char *name, expressionS *exp, char *cp,
-			segT (*operandf) (expressionS *))
+sh64_consume_datalabel (const char *name, expressionS *exp,
+			enum expr_mode mode, char *cp,
+			segT (*operandf) (expressionS *, enum expr_mode))
 {
   static int parsing_datalabel = 0;
 
@@ -3258,7 +3259,7 @@ sh64_consume_datalabel (const char *name
 
       *input_line_pointer = *cp;
       parsing_datalabel = 1;
-      (*operandf) (exp);
+      (*operandf) (exp, expr_normal);
       parsing_datalabel = save_parsing_datalabel;
 
       if (exp->X_op == O_symbol || exp->X_op == O_PIC_reloc)
@@ -3331,7 +3332,7 @@ sh64_consume_datalabel (const char *name
       return 1;
     }
 
-  return sh_parse_name (name, exp, cp);
+  return sh_parse_name (name, exp, mode, cp);
 }
 
 /* This function is called just before symbols are being output.  It
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-sh64.h	2005-09-26 08:41:25.000000000 +0200
+++ 2005-09-27/gas/config/tc-sh64.h	2005-09-29 13:07:07.000000000 +0200
@@ -124,10 +124,11 @@ extern int sh64_target_mach (void);
    expression, since we have handled it ourselves.  FIXME: What we really
    need is a new GAS infrastructure feature: md_qualifier.  */
 #undef md_parse_name
-#define md_parse_name(NAME, EXP, CP) \
- sh64_consume_datalabel (NAME, EXP, CP, operand)
-extern int sh64_consume_datalabel (const char *, expressionS *, char *,
-				   segT (*) (expressionS *));
+#define md_parse_name(NAME, EXP, MODE, CP) \
+ sh64_consume_datalabel (NAME, EXP, MODE, CP, operand)
+extern int sh64_consume_datalabel (const char *, expressionS *,
+				   enum expr_mode, char *,
+				   segT (*) (expressionS *, enum expr_mode));
 
 /* Saying "$" is the same as saying ".".  */
 #define DOLLAR_DOT
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/config/tc-tic54x.h	2005-08-18 08:51:32.000000000 +0200
+++ 2005-09-27/gas/config/tc-tic54x.h	2005-09-28 14:42:26.000000000 +0200
@@ -83,7 +83,7 @@ extern void tic54x_number_to_chars (char
 extern void tic54x_adjust_symtab (void);
 #define tc_unrecognized_line(ch) tic54x_unrecognized_line(ch)
 extern int tic54x_unrecognized_line (int ch);
-#define md_parse_name(s,e,c) tic54x_parse_name(s,e)
+#define md_parse_name(s,e,m,c) tic54x_parse_name(s,e)
 extern int tic54x_parse_name (char *name, expressionS *e);
 #define md_undefined_symbol(s) tic54x_undefined_symbol(s)
 extern symbolS *tic54x_undefined_symbol (char *name);
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/doc/as.texinfo	2005-09-26 08:41:26.000000000 +0200
+++ 2005-09-27/gas/doc/as.texinfo	2005-09-27 09:06:01.000000000 +0200
@@ -3119,7 +3119,9 @@ provides a special directive @code{.labe
 A symbol can be given an arbitrary value by writing a symbol, followed
 by an equals sign @samp{=}, followed by an expression
 (@pxref{Expressions}).  This is equivalent to using the @code{.set}
-directive.  @xref{Set,,@code{.set}}.
+directive.  @xref{Set,,@code{.set}}.  In the same way, using a double
+equals sign @samp{=}@samp{=} here represents an equivalent of the
+@code{.eqv} directive.  @xref{Eqv,,@code{.eqv}}.
 
 @node Symbol Names
 @section Symbol Names
@@ -3717,6 +3719,7 @@ Some machine configurations provide addi
 * Endif::                       @code{.endif}
 * Equ::                         @code{.equ @var{symbol}, @var{expression}}
 * Equiv::                       @code{.equiv @var{symbol}, @var{expression}}
+* Eqv::                         @code{.eqv @var{symbol}, @var{expression}}
 * Err::				@code{.err}
 * Error::			@code{.error @var{string}}
 * Exitm::			@code{.exitm}
@@ -4284,6 +4287,15 @@ Except for the contents of the error mes
 .endif
 .equ SYM,VAL
 @end smallexample
+plus it protects the symbol from later redefinition.
+
+@node Eqv
+@section @code{.eqv @var{symbol}, @var{expression}}
+@cindex @code{eqv} directive
+The @code{.eqv} directive is like @code{.equiv}, but no attempt is made to
+evaluate the expression or any part of it immediately.  Instead each time
+the resulting symbol is used in an expression, a snapshot of its current
+value is taken.
 
 @node Err
 @section @code{.err}
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/dw2gencfi.c	2005-08-18 08:51:26.000000000 +0200
+++ 2005-09-27/gas/dw2gencfi.c	2005-09-26 11:10:15.000000000 +0200
@@ -415,7 +415,7 @@ cfi_parse_reg (void)
     }
 #endif
 
-  expression (&exp);
+  expression_and_evaluate (&exp);
   switch (exp.X_op)
     {
     case O_register:
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/expr.c	2005-05-06 08:24:27.000000000 +0200
+++ 2005-09-27/gas/expr.c	2005-09-28 14:39:11.000000000 +0200
@@ -41,7 +41,7 @@ static void integer_constant (int radix,
 static void mri_char_constant (expressionS *);
 static void current_location (expressionS *);
 static void clean_up_expression (expressionS * expressionP);
-static segT operand (expressionS *);
+static segT operand (expressionS *, enum expr_mode);
 static operatorT operator (int *);
 
 extern const char EXP_CHARS[], FLT_CHARS[];
@@ -708,7 +708,7 @@ current_location (expressionS *expressio
 	Input_line_pointer->(next non-blank) char after operand.  */
 
 static segT
-operand (expressionS *expressionP)
+operand (expressionS *expressionP, enum expr_mode mode)
 {
   char c;
   symbolS *symbolP;	/* Points to symbol.  */
@@ -944,7 +944,10 @@ operand (expressionS *expressionP)
     case '[':
 #endif
       /* Didn't begin with digit & not a name.  */
-      segment = expression (expressionP);
+      if (mode != expr_defer)
+	segment = expression (expressionP);
+      else
+	segment = deferred_expression (expressionP);
       /* expression () will pass trailing whitespace.  */
       if ((c == '(' && *input_line_pointer != ')')
 	  || (c == '[' && *input_line_pointer != ']'))
@@ -1002,7 +1005,7 @@ operand (expressionS *expressionP)
 	if (0 && (c == '-' || c == '+') && *input_line_pointer == c)
 	  goto target_op;
 	
-	operand (expressionP);
+	operand (expressionP, mode);
 	if (expressionP->X_op == O_constant)
 	  {
 	    /* input_line_pointer -> char after operand.  */
@@ -1214,7 +1217,7 @@ operand (expressionS *expressionP)
 	     specially in certain contexts.  If a name always has a
 	     specific value, it can often be handled by simply
 	     entering it in the symbol table.  */
-	  if (md_parse_name (name, expressionP, &c))
+	  if (md_parse_name (name, expressionP, mode, &c))
 	    {
 	      *input_line_pointer = c;
 	      break;
@@ -1265,12 +1268,12 @@ operand (expressionS *expressionP)
 	  /* If we have an absolute symbol or a reg, then we know its
 	     value now.  */
 	  segment = S_GET_SEGMENT (symbolP);
-	  if (segment == absolute_section)
+	  if (mode != expr_defer && segment == absolute_section)
 	    {
 	      expressionP->X_op = O_constant;
 	      expressionP->X_add_number = S_GET_VALUE (symbolP);
 	    }
-	  else if (segment == reg_section)
+	  else if (mode != expr_defer && segment == reg_section)
 	    {
 	      expressionP->X_op = O_register;
 	      expressionP->X_add_number = S_GET_VALUE (symbolP);
@@ -1314,6 +1317,9 @@ operand (expressionS *expressionP)
   if (expressionP->X_add_symbol)
     symbol_mark_used (expressionP->X_add_symbol);
 
+  expressionP->X_add_symbol = symbol_clone_if_forward_ref (expressionP->X_add_symbol);
+  expressionP->X_op_symbol = symbol_clone_if_forward_ref (expressionP->X_op_symbol);
+
   switch (expressionP->X_op)
     {
     default:
@@ -1619,7 +1625,8 @@ operator (int *num_chars)
 
 segT
 expr (int rankarg,		/* Larger # is higher rank.  */
-      expressionS *resultP	/* Deliver result here.  */)
+      expressionS *resultP,	/* Deliver result here.  */
+      enum expr_mode mode	/* Controls behavior.  */)
 {
   operator_rankT rank = (operator_rankT) rankarg;
   segT retval;
@@ -1634,7 +1641,7 @@ expr (int rankarg,		/* Larger # is highe
   if (rank == 0)
     dot_value = frag_now_fix ();
 
-  retval = operand (resultP);
+  retval = operand (resultP, mode);
 
   /* operand () gobbles spaces.  */
   know (*input_line_pointer != ' ');
@@ -1646,7 +1653,7 @@ expr (int rankarg,		/* Larger # is highe
 
       input_line_pointer += op_chars;	/* -> after operator.  */
 
-      rightseg = expr (op_rank[(int) op_left], &right);
+      rightseg = expr (op_rank[(int) op_left], &right, mode);
       if (right.X_op == O_absent)
 	{
 	  as_warn (_("missing operand; zero assumed"));
@@ -1861,8 +1868,201 @@ expr (int rankarg,		/* Larger # is highe
   if (resultP->X_add_symbol)
     symbol_mark_used (resultP->X_add_symbol);
 
+  if (rank == 0 && mode == expr_evaluate)
+    resolve_expression (resultP);
+
   return resultP->X_op == O_constant ? absolute_section : retval;
 }
+
+/* Resolve an expression without changing any symbols/sub-expressions
+   used.  */
+
+int
+resolve_expression (expressionS *expressionP)
+{
+  /* Help out with CSE.  */
+  valueT final_val = expressionP->X_add_number;
+  symbolS *add_symbol = expressionP->X_add_symbol;
+  symbolS *op_symbol = expressionP->X_op_symbol;
+  operatorT op = expressionP->X_op;
+  valueT left, right;
+  segT seg_left, seg_right;
+  fragS *frag_left, *frag_right;
+
+  switch (op)
+    {
+    default:
+      return 0;
+
+    case O_constant:
+    case O_register:
+      left = 0;
+      break;
+
+    case O_symbol:
+    case O_symbol_rva:
+      if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left))
+	return 0;
+
+      break;
+
+    case O_uminus:
+    case O_bit_not:
+    case O_logical_not:
+      if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left))
+	return 0;
+
+      if (seg_left != absolute_section)
+	return 0;
+
+      if (op == O_logical_not)
+	left = !left;
+      else if (op == O_uminus)
+	left = -left;
+      else
+	left = ~left;
+      op = O_constant;
+      break;
+
+    case O_multiply:
+    case O_divide:
+    case O_modulus:
+    case O_left_shift:
+    case O_right_shift:
+    case O_bit_inclusive_or:
+    case O_bit_or_not:
+    case O_bit_exclusive_or:
+    case O_bit_and:
+    case O_add:
+    case O_subtract:
+    case O_eq:
+    case O_ne:
+    case O_lt:
+    case O_le:
+    case O_ge:
+    case O_gt:
+    case O_logical_and:
+    case O_logical_or:
+      if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left)
+	  || !snapshot_symbol (op_symbol, &right, &seg_right, &frag_right))
+	return 0;
+
+      /* Simplify addition or subtraction of a constant by folding the
+	 constant into X_add_number.  */
+      if (op == O_add)
+	{
+	  if (seg_right == absolute_section)
+	    {
+	      final_val += right;
+	      op = O_symbol;
+	      break;
+	    }
+	  else if (seg_left == absolute_section)
+	    {
+	      final_val += left;
+	      left = right;
+	      seg_left = seg_right;
+	      expressionP->X_add_symbol = expressionP->X_op_symbol;
+	      op = O_symbol;
+	      break;
+	    }
+	}
+      else if (op == O_subtract)
+	{
+	  if (seg_right == absolute_section)
+	    {
+	      final_val -= right;
+	      op = O_symbol;
+	      break;
+	    }
+	}
+
+      /* Equality and non-equality tests are permitted on anything.
+	 Subtraction, and other comparison operators are permitted if
+	 both operands are in the same section.  Otherwise, both
+	 operands must be absolute.  We already handled the case of
+	 addition or subtraction of a constant above.  */
+      if (!(seg_left == absolute_section
+	       && seg_right == absolute_section)
+	  && !(op == O_eq || op == O_ne)
+	  && !((op == O_subtract
+		|| op == O_lt || op == O_le || op == O_ge || op == O_gt)
+	       && seg_left == seg_right
+	       && (finalize_syms || frag_left == frag_right)
+	       && ((seg_left != undefined_section
+		    && seg_left != reg_section)
+		   || add_symbol == op_symbol)))
+	return 0;
+
+      switch (op)
+	{
+	case O_add:			left += right; break;
+	case O_subtract:		left -= right; break;
+	case O_multiply:		left *= right; break;
+	case O_divide:
+	  if (right == 0)
+	    return 0;
+	  left = (offsetT) left / (offsetT) right;
+	  break;
+	case O_modulus:
+	  if (right == 0)
+	    return 0;
+	  left = (offsetT) left % (offsetT) right;
+	  break;
+	case O_left_shift:		left <<= right; break;
+	case O_right_shift:		left >>= right; break;
+	case O_bit_inclusive_or:	left |= right; break;
+	case O_bit_or_not:		left |= ~right; break;
+	case O_bit_exclusive_or:	left ^= right; break;
+	case O_bit_and:			left &= right; break;
+	case O_eq:
+	case O_ne:
+	  left = (left == right
+		  && seg_left == seg_right
+		  && (finalize_syms || frag_left == frag_right)
+		  && ((seg_left != undefined_section
+		       && seg_left != reg_section)
+		      || add_symbol == op_symbol)
+		  ? ~ (valueT) 0 : 0);
+	  if (op == O_ne)
+	    left = ~left;
+	  break;
+	case O_lt:
+	  left = (offsetT) left <  (offsetT) right ? ~ (valueT) 0 : 0;
+	  break;
+	case O_le:
+	  left = (offsetT) left <= (offsetT) right ? ~ (valueT) 0 : 0;
+	  break;
+	case O_ge:
+	  left = (offsetT) left >= (offsetT) right ? ~ (valueT) 0 : 0;
+	  break;
+	case O_gt:
+	  left = (offsetT) left >  (offsetT) right ? ~ (valueT) 0 : 0;
+	  break;
+	case O_logical_and:	left = left && right; break;
+	case O_logical_or:	left = left || right; break;
+	default:		abort ();
+	}
+
+      op = O_constant;
+      break;
+    }
+
+  if (op == O_symbol)
+    {
+      if (seg_left == absolute_section)
+	op = O_constant;
+      else if (seg_left == reg_section && final_val == 0)
+	op = O_register;
+    }
+  expressionP->X_op = op;
+
+  if (op == O_constant || op == O_register)
+    final_val += left;
+  expressionP->X_add_number = final_val;
+
+  return 1;
+}
 
 /* This lives here because it belongs equally in expr.c & read.c.
    expr.c is just a branch office read.c anyway, and putting it
@@ -1899,6 +2099,6 @@ unsigned int
 get_single_number (void)
 {
   expressionS exp;
-  operand (&exp);
+  operand (&exp, expr_normal);
   return exp.X_add_number;
 }
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/expr.h	2005-05-06 08:24:27.000000000 +0200
+++ 2005-09-27/gas/expr.h	2005-09-26 11:08:48.000000000 +0200
@@ -143,8 +143,17 @@ typedef struct expressionS {
   unsigned short X_md;
 } expressionS;
 
+enum expr_mode
+{
+  expr_evaluate,
+  expr_normal,
+  expr_defer
+};
+
 /* "result" should be type (expressionS *).  */
-#define expression(result) expr (0, result)
+#define expression(result) expr (0, result, expr_normal)
+#define expression_and_evaluate(result) expr (0, result, expr_evaluate)
+#define deferred_expression(result) expr (0, result, expr_defer)
 
 /* If an expression is O_big, look here for its value. These common
    data may be clobbered whenever expr() is called.  */
@@ -160,10 +169,12 @@ typedef char operator_rankT;
 extern char get_symbol_end (void);
 extern void expr_begin (void);
 extern void expr_set_precedence (void);
-extern segT expr (int rank, expressionS * resultP);
+extern segT expr (int, expressionS *, enum expr_mode);
 extern unsigned int get_single_number (void);
 extern symbolS *make_expr_symbol (expressionS * expressionP);
 extern int expr_symbol_where (symbolS *, char **, unsigned int *);
 
 extern symbolS *expr_build_uconstant (offsetT);
 extern symbolS *expr_build_dot (void);
+
+int resolve_expression (expressionS *);
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/read.c	2005-09-26 08:41:19.000000000 +0200
+++ 2005-09-27/gas/read.c	2005-09-29 09:36:03.000000000 +0200
@@ -323,6 +323,7 @@ static const pseudo_typeS potable[] = {
 /* endef  */
   {"equ", s_set, 0},
   {"equiv", s_set, 1},
+  {"eqv", s_set, -1},
   {"err", s_err, 0},
   {"error", s_errwarn, 1},
   {"exitm", s_mexit, 0},
@@ -441,7 +442,7 @@ static const pseudo_typeS potable[] = {
 static offsetT
 get_absolute_expr (expressionS *exp)
 {
-  expression (exp);
+  expression_and_evaluate (exp);
   if (exp->X_op != O_constant)
     {
       if (exp->X_op != O_absent)
@@ -787,6 +788,14 @@ read_a_source_file (char *name)
 		  /* Input_line_pointer->after ':'.  */
 		  SKIP_WHITESPACE ();
 		}
+              else if (input_line_pointer[1] == '='
+		       && (c == '='
+			   || ((c == ' ' || c == '\t')
+			       && input_line_pointer[2] == '=')))
+		{
+		  equals (s, -1);
+		  demand_empty_rest_of_line ();
+		}
               else if ((c == '='
                        || ((c == ' ' || c == '\t')
                             && input_line_pointer[1] == '='))
@@ -2210,7 +2219,7 @@ s_lsym (int ignore ATTRIBUTE_UNUSED)
     }
 
   input_line_pointer++;
-  expression (&exp);
+  expression_and_evaluate (&exp);
 
   if (exp.X_op != O_constant
       && exp.X_op != O_register)
@@ -2743,7 +2752,7 @@ end_repeat (int extra)
 }
 
 static void
-assign_symbol (char *name, int no_reassign)
+assign_symbol (char *name, int mode)
 {
   symbolS *symbolP;
 
@@ -2784,18 +2793,37 @@ assign_symbol (char *name, int no_reassi
 #endif
     }
 
-  /* Permit register names to be redefined.  */
-  if (no_reassign
-      && S_IS_DEFINED (symbolP)
-      && S_GET_SEGMENT (symbolP) != reg_section)
-    as_bad (_("symbol `%s' is already defined"), name);
+  if (S_IS_DEFINED (symbolP))
+    {
+      /* Permit register names to be redefined.  */
+      if ((mode != 0 || !S_IS_VOLATILE (symbolP))
+	  && S_GET_SEGMENT (symbolP) != reg_section)
+	{
+	  as_bad (_("symbol `%s' is already defined"), name);
+	  symbolP = symbol_clone (symbolP, 0);
+	}
+      /* If the symbol is volatile, copy the symbol and replace the
+	 original with the copy, so that previous uses of the symbol will
+	 retain the value of the symbol at the point of use.  */
+      else if (S_IS_VOLATILE (symbolP)
+	  /* This could be avoided when the symbol wasn't used so far, but
+	     the comment in struc-symbol.h says this flag isn't reliable.  */
+	  && (1 || symbol_used_p (symbolP)))
+	symbolP = symbol_clone (symbolP, 1);
+    }
+
+  if (mode == 0)
+    S_SET_VOLATILE (symbolP);
+  else if (mode < 0)
+    S_SET_FORWARD_REF (symbolP);
 
   pseudo_set (symbolP);
 }
 
-/* Handle the .equ, .equiv and .set directives.  If EQUIV is 1, then
-   this is .equiv, and it is an error if the symbol is already
-   defined.  */
+/* Handle the .equ, .equiv, .eqv, and .set directives.  If EQUIV is 1,
+   then this is .equiv, and it is an error if the symbol is already
+   defined.  If EQUIV is -1, the symbol additionally is a forward
+   reference.  */
 
 void
 s_set (int equiv)
@@ -2917,6 +2945,7 @@ s_space (int mult)
       || val.X_add_number > 0xff
       || (mult != 0 && mult != 1 && val.X_add_number != 0))
     {
+      resolve_expression (&exp);
       if (exp.X_op != O_constant)
 	as_bad (_("unsupported variable size or fill value"));
       else
@@ -2932,6 +2961,9 @@ s_space (int mult)
     }
   else
     {
+      if (now_seg == absolute_section || mri_common_symbol != NULL)
+	resolve_expression (&exp);
+
       if (exp.X_op == O_constant)
 	{
 	  long repeat;
@@ -3185,7 +3217,10 @@ pseudo_set (symbolS *symbolP)
 
   know (symbolP);		/* NULL pointer is logic error.  */
 
-  (void) expression (&exp);
+  if (!S_IS_FORWARD_REF (symbolP))
+    (void) expression (&exp);
+  else
+    (void) deferred_expression (&exp);
 
   if (exp.X_op == O_illegal)
     as_bad (_("illegal expression"));
@@ -3199,6 +3234,7 @@ pseudo_set (symbolS *symbolP)
 	as_bad (_("floating point number invalid"));
     }
   else if (exp.X_op == O_subtract
+	   && !S_IS_FORWARD_REF (symbolP)
 	   && SEG_NORMAL (S_GET_SEGMENT (exp.X_add_symbol))
 	   && (symbol_get_frag (exp.X_add_symbol)
 	       == symbol_get_frag (exp.X_op_symbol)))
@@ -3245,7 +3281,7 @@ pseudo_set (symbolS *symbolP)
 	  *symbol_X_add_number (symbolP) += exp.X_add_number;
 	  break;
 	}
-      else if (seg != undefined_section)
+      else if (!S_IS_FORWARD_REF (symbolP) && seg != undefined_section)
 	{
 	  symbolS *s = exp.X_add_symbol;
 
@@ -4838,6 +4874,8 @@ equals (char *sym_name, int reassign)
   input_line_pointer++;
   if (*input_line_pointer == '=')
     input_line_pointer++;
+  if (reassign < 0 && *input_line_pointer == '=')
+    input_line_pointer++;
 
   while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
     input_line_pointer++;
@@ -4845,7 +4883,7 @@ equals (char *sym_name, int reassign)
   if (flag_mri)
     stop = mri_comment_field (&stopc);
 
-  assign_symbol (sym_name, !reassign);
+  assign_symbol (sym_name, reassign >= 0 ? !reassign : reassign);
 
   if (flag_mri)
     {
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/struc-symbol.h	2005-08-18 08:51:26.000000000 +0200
+++ 2005-09-27/gas/struc-symbol.h	2005-09-26 15:30:25.000000000 +0200
@@ -60,6 +60,12 @@ struct symbol
      a symbol is used in backend routines.  */
   unsigned int sy_used : 1;
 
+  /* Whether the symbol can be re-defined.  */
+  unsigned int sy_volatile : 1;
+
+  /* Whether the symbol is a forward reference.  */
+  unsigned int sy_forward_ref : 1;
+
   /* This is set if the symbol is defined in an MRI common section.
      We handle such sections as single common symbols, so symbols
      defined within them must be treated specially by the relocation
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/symbols.c	2005-08-18 08:51:26.000000000 +0200
+++ 2005-09-27/gas/symbols.c	2005-09-29 15:22:26.000000000 +0200
@@ -538,6 +538,94 @@ symbol_make (const char *name)
 }
 
 symbolS *
+symbol_clone (symbolS *orgsymP, int replace)
+{
+  symbolS *newsymP;
+
+  /* Running local_symbol_convert on a clone that's not the one currently
+     in local_hash would incorrectly replace the hash entry.  Thus the
+     symbol must be converted here.  Note that the rest of the function
+     depends on not encountering an unconverted symbol.  */
+  if (LOCAL_SYMBOL_CHECK (orgsymP))
+    orgsymP = local_symbol_convert ((struct local_symbol *) orgsymP);
+
+  know (S_IS_DEFINED (orgsymP));
+
+  newsymP = obstack_alloc (&notes, sizeof (*newsymP));
+  *newsymP = *orgsymP;
+
+  if (replace)
+    {
+      if (symbol_rootP == orgsymP)
+	symbol_rootP = newsymP;
+      else if (orgsymP->sy_previous)
+	{
+	  orgsymP->sy_previous->sy_next = newsymP;
+	  orgsymP->sy_previous = NULL;
+	}
+      if (symbol_lastP == orgsymP)
+	symbol_lastP = newsymP;
+      else if (orgsymP->sy_next)
+	orgsymP->sy_next->sy_previous = newsymP;
+      orgsymP->sy_next = NULL;
+      debug_verify_symchain (symbol_rootP, symbol_lastP);
+
+      symbol_table_insert (newsymP);
+    }
+
+  return newsymP;
+}
+
+/* Referenced symbols, if they are forward references, need to be cloned
+   (without replacing the original) so that the value of the referenced
+   symbols at the point of use .  */
+
+#undef symbol_clone_if_forward_ref
+symbolS *
+symbol_clone_if_forward_ref (symbolS *symbolP, int is_forward)
+{
+  if (symbolP && !LOCAL_SYMBOL_CHECK (symbolP))
+    {
+      symbolS *add_symbol = symbolP->sy_value.X_add_symbol;
+      symbolS *op_symbol = symbolP->sy_value.X_op_symbol;
+
+      if (symbolP->sy_forward_ref)
+	is_forward = 1;
+
+      if (is_forward)
+	{
+	  /* assign_symbol() clones volatile symbols; pre-existing expressions
+	     hold references to the original instance, but want the current
+	     value.  Just repeat the lookup.  */
+	  if (add_symbol && S_IS_VOLATILE (add_symbol))
+	    add_symbol = symbol_find_exact (S_GET_NAME (add_symbol));
+	  if (op_symbol && S_IS_VOLATILE (op_symbol))
+	    op_symbol = symbol_find_exact (S_GET_NAME (op_symbol));
+	}
+
+      /* Re-using sy_resolving here, as this routine cannot get called from
+	 symbol resolution code.  */
+      if (symbolP->bsym->section == expr_section && !symbolP->sy_resolving)
+	{
+	  symbolP->sy_resolving = 1;
+	  add_symbol = symbol_clone_if_forward_ref (add_symbol, is_forward);
+	  op_symbol = symbol_clone_if_forward_ref (op_symbol, is_forward);
+	  symbolP->sy_resolving = 0;
+	}
+
+      if (symbolP->sy_forward_ref
+	  || add_symbol != symbolP->sy_value.X_add_symbol
+	  || op_symbol != symbolP->sy_value.X_op_symbol)
+	symbolP = symbol_clone (symbolP, 0);
+
+      symbolP->sy_value.X_add_symbol = add_symbol;
+      symbolP->sy_value.X_op_symbol = op_symbol;
+    }
+
+  return symbolP;
+}
+
+symbolS *
 symbol_temp_new (segT seg, valueT ofs, fragS *frag)
 {
   return symbol_new (FAKE_LABEL_NAME, seg, ofs, frag);
@@ -1189,6 +1277,71 @@ resolve_local_symbol_values (void)
   hash_traverse (local_hash, resolve_local_symbol);
 }
 
+/* Obtain the current value of a symbol without changing any
+   sub-expressions used.  */
+
+int
+snapshot_symbol (symbolS *symbolP, valueT *valueP, segT *segP, fragS **fragPP)
+{
+  if (LOCAL_SYMBOL_CHECK (symbolP))
+    {
+      struct local_symbol *locsym = (struct local_symbol *) symbolP;
+
+      *valueP = locsym->lsy_value;
+      *segP = locsym->lsy_section;
+      *fragPP = local_symbol_get_frag (locsym);
+    }
+  else
+    {
+      expressionS expr = symbolP->sy_value;
+
+      if (!symbolP->sy_resolved && expr.X_op != O_illegal)
+	{
+	  int resolved;
+
+	  if (symbolP->sy_resolving)
+	    return 0;
+	  symbolP->sy_resolving = 1;
+	  resolved = resolve_expression (&expr);
+	  symbolP->sy_resolving = 0;
+	  if (!resolved)
+	    return 0;
+
+	  switch (expr.X_op)
+	    {
+	    case O_constant:
+	    case O_register:
+	      /* This check wouldn't be needed if pseudo_set() didn't set
+		 symbols equated to bare symbols to undefined_section.  */
+	      if (symbolP->bsym->section != undefined_section
+		  || symbolP->sy_value.X_op != O_symbol)
+		break;
+	      /* Fall thru.  */
+	    case O_symbol:
+	    case O_symbol_rva:
+	      symbolP = expr.X_add_symbol;
+	      break;
+	    default:
+	      return 0;
+	    }
+	}
+
+      *valueP = expr.X_add_number;
+      *segP = symbolP->bsym->section;
+      *fragPP = symbolP->sy_frag;
+
+      if (*segP == expr_section)
+	switch (expr.X_op)
+	  {
+	  case O_constant: *segP = absolute_section; break;
+	  case O_register: *segP = reg_section; break;
+	  default: break;
+	  }
+    }
+
+  return 1;
+}
+
 /* Dollar labels look like a number followed by a dollar sign.  Eg, "42$".
    They are *really* local.  That is, they go out of scope whenever we see a
    label that isn't local.  Also, like fb labels, there can be multiple
@@ -1747,6 +1900,22 @@ S_IS_STABD (symbolS *s)
   return S_GET_NAME (s) == 0;
 }
 
+int
+S_IS_VOLATILE (const symbolS *s)
+{
+  if (LOCAL_SYMBOL_CHECK (s))
+    return 0;
+  return s->sy_volatile;
+}
+
+int
+S_IS_FORWARD_REF (const symbolS *s)
+{
+  if (LOCAL_SYMBOL_CHECK (s))
+    return 0;
+  return s->sy_forward_ref;
+}
+
 const char *
 S_GET_NAME (symbolS *s)
 {
@@ -1872,6 +2041,22 @@ S_SET_NAME (symbolS *s, const char *name
   s->bsym->name = name;
 }
 
+void
+S_SET_VOLATILE (symbolS *s)
+{
+  if (LOCAL_SYMBOL_CHECK (s))
+    s = local_symbol_convert ((struct local_symbol *) s);
+  s->sy_volatile = 1;
+}
+
+void
+S_SET_FORWARD_REF (symbolS *s)
+{
+  if (LOCAL_SYMBOL_CHECK (s))
+    s = local_symbol_convert ((struct local_symbol *) s);
+  s->sy_forward_ref = 1;
+}
+
 /* Return the previous symbol in a chain.  */
 
 symbolS *
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/symbols.h	2005-08-18 08:51:26.000000000 +0200
+++ 2005-09-27/gas/symbols.h	2005-09-27 18:13:17.000000000 +0200
@@ -44,6 +44,10 @@ symbolS *symbol_new (const char *name, s
 		     fragS * frag);
 symbolS *symbol_create (const char *name, segT segment, valueT value,
 			fragS * frag);
+symbolS *symbol_clone (symbolS *, int);
+#undef symbol_clone_if_forward_ref
+symbolS *symbol_clone_if_forward_ref (symbolS *, int);
+#define symbol_clone_if_forward_ref(s) symbol_clone_if_forward_ref (s, 0)
 symbolS *symbol_temp_new (segT, valueT, fragS *);
 symbolS *symbol_temp_new_now (void);
 symbolS *symbol_temp_make (void);
@@ -55,6 +59,7 @@ void symbol_print_statistics (FILE *);
 void symbol_table_insert (symbolS * symbolP);
 valueT resolve_symbol_value (symbolS *);
 void resolve_local_symbol_values (void);
+int snapshot_symbol (symbolS *, valueT *, segT *, fragS **);
 
 void print_symbol_value (symbolS *);
 void print_expr (expressionS *);
@@ -84,6 +89,8 @@ extern int S_FORCE_RELOC (symbolS *, int
 extern int S_IS_DEBUG (symbolS *);
 extern int S_IS_LOCAL (symbolS *);
 extern int S_IS_STABD (symbolS *);
+extern int S_IS_VOLATILE (const symbolS *);
+extern int S_IS_FORWARD_REF (const symbolS *);
 extern const char *S_GET_NAME (symbolS *);
 extern segT S_GET_SEGMENT (symbolS *);
 extern void S_SET_SEGMENT (symbolS *, segT);
@@ -92,6 +99,8 @@ extern void S_SET_NAME (symbolS *, const
 extern void S_CLEAR_EXTERNAL (symbolS *);
 extern void S_SET_WEAK (symbolS *);
 extern void S_SET_THREAD_LOCAL (symbolS *);
+extern void S_SET_VOLATILE (symbolS *);
+extern void S_SET_FORWARD_REF (symbolS *);
 
 #ifndef WORKING_DOT_WORD
 struct broken_word
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/assign-bad.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/assign-bad.s	2005-09-28 10:47:57.000000000 +0200
@@ -0,0 +1,2 @@
+ yyy == 3
+ yyy == 4
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/assign-ok.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/assign-ok.s	2005-09-28 10:48:01.000000000 +0200
@@ -0,0 +1,3 @@
+ xxx = 1
+ xxx = 2
+ yyy == 3
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/cond.d	2005-04-26 17:33:32.000000000 +0200
+++ 2005-09-27/gas/testsuite/gas/all/cond.d	2005-09-26 11:50:46.000000000 +0200
@@ -24,6 +24,12 @@
   29[ 	]+.else
   31[ 	]+.endif
 [ 	]*[1-9][0-9]*[ 	]+
+[ 	]*[1-9][0-9]*[ 	]+\.equiv[ 	]+x,[ 	]*y[ 	]*
+[ 	]*[1-9][0-9]*[ 	]+\.equiv[ 	]+y,[ 	]*0[ 	]*
+[ 	]*[1-9][0-9]*[ 	]+\.if[ 	]+x[ 	]*
+[ 	]*[1-9][0-9]*[ 	]+\.elseif[ 	]+x[ 	]*
+[ 	]*[1-9][0-9]*[ 	]+\.endif[ 	]*
+[ 	]*[1-9][0-9]*[ 	]+
 [ 	]*[1-9][0-9]*[ 	]+\.macro[ 	]+m[ 	]+x,[ 	]*y[ 	]*
 #...
 [ 	]*[1-9][0-9]*[ 	]+\.endm[ 	]*
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/cond.s	2005-04-26 17:19:49.000000000 +0200
+++ 2005-09-27/gas/testsuite/gas/all/cond.s	2005-09-26 11:50:22.000000000 +0200
@@ -30,6 +30,14 @@
 	.long	9
 	.endif
 
+	.equiv	x, y
+	.equiv	y, 0
+	.if	x
+	.err
+	.elseif	x
+	.err
+	.endif
+
 	.macro	m x, y
 	.ifb \x
 	.long	-1
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/equ-bad.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/equ-bad.s	2005-09-26 12:06:01.000000000 +0200
@@ -0,0 +1,2 @@
+ .equ x, 1
+ .eqv x, 2
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/equ-ok.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/equ-ok.s	2005-09-26 12:05:53.000000000 +0200
@@ -0,0 +1,2 @@
+ .equ x, 1
+ .equ x, 2
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/equiv1.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/equiv1.s	2005-09-29 18:10:32.000000000 +0200
@@ -0,0 +1,5 @@
+;# Re-definition of an already .equiv-ed symbol (to another symbol).
+;# The assembler should reject this.
+ .equiv x, y
+ .equiv y, 1
+ .equiv x, 0
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/equiv2.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/equiv2.s	2005-09-29 18:10:37.000000000 +0200
@@ -0,0 +1,6 @@
+;# Re-definition of an already .equiv-ed symbol (to an expression).
+;# The assembler should reject this.
+ .equiv x, y-z
+ .equiv y, 1
+ .equiv z, 1
+ .equiv x, 1
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/eqv-bad.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/eqv-bad.s	2005-09-26 12:06:14.000000000 +0200
@@ -0,0 +1,2 @@
+ .eqv x, 1
+ .eqv x, 2
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/eqv-ok.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/eqv-ok.s	2005-09-26 12:06:22.000000000 +0200
@@ -0,0 +1 @@
+ .eqv x, 1
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/forward.d	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/forward.d	2005-09-28 14:13:49.000000000 +0200
@@ -0,0 +1,8 @@
+#objdump: -s -j .data
+#name: forward references
+
+.*: .*
+
+Contents of section .data:
+ 0000 01020304 ff0203fc 01020304 ff0203fc  ................
+#pass
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/forward.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/forward.s	2005-09-26 16:27:39.000000000 +0200
@@ -0,0 +1,40 @@
+ .equiv two, 2*one
+ .equiv minus_one, -one
+ .equ one, 1
+ .equiv three, 3*one
+ .eqv four, 4*one
+
+ .data
+
+ .if two > one
+  .byte one
+  .byte two
+ .endif
+ .if four > one
+  .byte three
+  .byte four
+ .endif
+
+ .equ one, -1
+ .byte one
+ .byte two
+ .if four < one
+  .byte three
+  .byte four
+ .endif
+
+ .equ one, -minus_one
+ .byte one
+ .byte two
+ .if four > one
+  .byte three
+  .byte four
+ .endif
+
+ .equ one, minus_one
+ .byte one
+ .byte two
+ .if four < one
+  .byte three
+  .byte four
+ .endif
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/gas.exp	2005-08-18 08:51:33.000000000 +0200
+++ 2005-09-27/gas/testsuite/gas/all/gas.exp	2005-09-29 13:57:18.000000000 +0200
@@ -34,6 +34,54 @@ if ![istarget hppa*-*-*] then {
     gas_test_error "diff1.s" "" "difference of two undefined symbols"
 }
 
+# PR/1387
+setup_xfail "*-*-*"
+gas_test_error "equiv1.s" "" ".equiv for symbol already set to another one"
+gas_test_error "equiv2.s" "" ".equiv for symbol already set to an expression"
+
+# .equ works differently on some targets.
+case $target_triplet in {
+    { hppa*-*-* } { }
+    { *c54x*-*-* } { }
+    default {
+	gas_test "equ-ok.s" "" "" ".equ for symbol already set"
+	gas_test_error "equ-bad.s" "" ".equ for symbol already set through .eqv"
+    }
+}
+
+gas_test "eqv-ok.s" "" "" ".eqv support"
+gas_test_error "eqv-bad.s" "" ".eqv for symbol already set"
+
+gas_test "assign-ok.s" "" "" "== assignment support"
+gas_test_error "assign-bad.s" "" "== assignment for symbol already set"
+
+# .equ works differently on some targets.
+# linkrelax-ing prevents most forward references from working.
+case $target_triplet in {
+    { crx*-*-* } { }
+    { h8300*-*-* } { }
+    { hppa*-*-* } { }
+    { mn10\[23\]00*-*-* } { }
+    { *c54x*-*-* } { }
+    default {
+	# Some targets don't manage to resolve BFD_RELOC_8 for constants.
+	setup_xfail "alpha*-*-*" "*c30*-*-*" "*c4x*-*-*" "d\[13\]0v*-*-*" "i860-*-*" "mips*-*-*" "sparc*-*-*"
+	run_dump_test forward
+    }
+}
+
+# .set works differently on some targets.
+case $target_triplet in {
+    { alpha*-*-* } { }
+    { iq2000*-*-* } { }
+    { mips*-*-* } { }
+    { *c54x*-*-* } { }
+    default {
+	setup_xfail "*c30*-*-*" "*c4x*-*-*"
+	run_dump_test redef
+    }
+}
+
 proc do_comment {} {
     set testname "comment.s: comments in listings"
     set x1 0
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/redef.d	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/redef.d	2005-09-28 14:13:43.000000000 +0200
@@ -0,0 +1,8 @@
+#objdump: -s -j .data
+#name: .equ redefinitions
+
+.*: .*
+
+Contents of section .data:
+ 0000 00000000 0[04]00000[04] 0[08]00000[08] 0[0c]00000[0c][ 	]+................[ 	]*
+#pass
--- /home/jbeulich/src/binutils/mainline/2005-09-27/gas/testsuite/gas/all/redef.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-09-27/gas/testsuite/gas/all/redef.s	2005-09-28 13:24:35.000000000 +0200
@@ -0,0 +1,11 @@
+ .data
+_start:
+ .set x, .-_start
+ .long x
+ .balign 4
+ .set x, .-_start
+ .long x
+ .set x, .-_start
+ .long x
+ .set x, .-_start
+ .long x
